From ecd352bd69b2f091ac2777c6d5f8850e1ac9dd28 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 13 Sep 2022 21:17:36 +0200 Subject: [PATCH 01/47] Initial commit on Onion transport for libp2p --- Cargo.toml | 3 +- transports/onion/Cargo.toml | 30 ++++++++ transports/onion/src/address.rs | 34 +++++++++ transports/onion/src/async_io.rs | 71 ++++++++++++++++++ transports/onion/src/lib.rs | 124 +++++++++++++++++++++++++++++++ transports/onion/src/tokio.rs | 67 +++++++++++++++++ transports/onion/tests/mod.rs | 26 +++++++ 7 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 transports/onion/Cargo.toml create mode 100644 transports/onion/src/address.rs create mode 100644 transports/onion/src/async_io.rs create mode 100644 transports/onion/src/lib.rs create mode 100644 transports/onion/src/tokio.rs create mode 100644 transports/onion/tests/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 89e63a4e5cd..1cafbb1482f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -154,7 +154,8 @@ members = [ "transports/tcp", "transports/uds", "transports/websocket", - "transports/wasm-ext" + "transports/wasm-ext", + "transports/onion" ] [[example]] diff --git a/transports/onion/Cargo.toml b/transports/onion/Cargo.toml new file mode 100644 index 00000000000..33ee5cd61b7 --- /dev/null +++ b/transports/onion/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "libp2p-onion" +version = "0.1.0" +edition = "2021" +license = "MIT" +resolver = "2" + + +[dependencies] +libp2p-core = { version = "0.36.0", path = "../../core", default-features = false } +futures = "0.3" +arti-client = { version = "0.6", default-features = false } +thiserror = "1" +tor-rtcompat = "0.6" +tokio-crate = { package = "tokio", version = "1", optional = true, default-features = false } +async-std-crate = { package = "async-std", version = "1", optional = true, default-features = false } + +[dev-dependencies] +libp2p = { version = "0.49", path = "../../" } +tokio-crate = { package = "tokio", version = "1", features = ["rt", "macros"] } +async-std-crate = { package = "async-std", version = "1", features = ["attributes"] } + + +[features] +default = ["async-std", "native-tls"] +tokio = ["arti-client/tokio", "dep:tokio-crate"] +async-std = ["arti-client/async-std"] +native-tls = ["arti-client/native-tls"] +rustls = ["arti-client/rustls"] + diff --git a/transports/onion/src/address.rs b/transports/onion/src/address.rs new file mode 100644 index 00000000000..08d90236128 --- /dev/null +++ b/transports/onion/src/address.rs @@ -0,0 +1,34 @@ +use std::net::{IpAddr, SocketAddr}; + +use arti_client::{TorAddr, TorAddrError}; +use libp2p_core::{multiaddr::Protocol, Multiaddr}; + +fn try_extract_socket_addr(mutliaddr: &Multiaddr) -> Result { + let mut ip_4 = None; + let mut ip_6 = None; + let mut tcp_port_opt = None; + for e in mutliaddr.iter() { + match e { + Protocol::Ip4(a) => { + ip_4 = Some(IpAddr::V4(a)); + } + Protocol::Ip6(a) => { + ip_6 = Some(IpAddr::V6(a)); + } + Protocol::Tcp(p) => { + tcp_port_opt = Some(p); + } + _ => {} + } + } + let ip = ip_4.or(ip_6).ok_or(TorAddrError::InvalidHostname)?; + let tcp_port = tcp_port_opt.ok_or(TorAddrError::NoPort)?; + Ok(SocketAddr::new(ip, tcp_port)) +} + +pub(super) fn dangerous_extract_tor_address( + multiaddr: &Multiaddr, +) -> Result { + let socket_addr = try_extract_socket_addr(multiaddr)?; + TorAddr::dangerously_from(socket_addr) +} diff --git a/transports/onion/src/async_io.rs b/transports/onion/src/async_io.rs new file mode 100644 index 00000000000..99889bdd841 --- /dev/null +++ b/transports/onion/src/async_io.rs @@ -0,0 +1,71 @@ +use std::pin::Pin; + +use arti_client::DataStream; +use futures::{AsyncRead, AsyncWrite}; + +pub struct OnionStream { + inner: DataStream, +} + +impl OnionStream { + #[inline] + pub(super) fn new(inner: DataStream) -> Self { + Self { inner } + } +} + +impl AsyncRead for OnionStream { + #[inline] + fn poll_read( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + Pin::new(&mut self.inner).poll_read(cx, buf) + } + + #[inline] + fn poll_read_vectored( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + bufs: &mut [std::io::IoSliceMut<'_>], + ) -> std::task::Poll> { + Pin::new(&mut self.inner).poll_read_vectored(cx, bufs) + } +} + +impl AsyncWrite for OnionStream { + #[inline] + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + Pin::new(&mut self.inner).poll_write(cx, buf) + } + + #[inline] + fn poll_flush( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + Pin::new(&mut self.inner).poll_flush(cx) + } + + #[inline] + fn poll_close( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + Pin::new(&mut self.inner).poll_close(cx) + } + + #[inline] + fn poll_write_vectored( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + bufs: &[std::io::IoSlice<'_>], + ) -> std::task::Poll> { + Pin::new(&mut self).poll_write_vectored(cx, bufs) + } +} diff --git a/transports/onion/src/lib.rs b/transports/onion/src/lib.rs new file mode 100644 index 00000000000..e4052cd7b1e --- /dev/null +++ b/transports/onion/src/lib.rs @@ -0,0 +1,124 @@ +#[cfg(all(feature = "async-std", feature = "tokio"))] +compile_error!("The features `async-std` and `tokio` are mutually exclusive"); + +// this is somewhat unnecessary, since arti-client won't compile before this is evaluated. +#[cfg(not(any(feature = "async-std", feature = "tokio")))] +compile_error!("Either one of the features `async-std` or `tokio` have to be enabled"); + +#[cfg(all(feature = "native-tls", feature = "rustls"))] +compile_error!("The features `native-tls` and `tokio` are mutually exclusive"); + +use core::pin::Pin; +use std::sync::Arc; + +use address::dangerous_extract_tor_address; +use arti_client::{TorAddrError, TorClient, TorClientBuilder}; +use futures::{future::BoxFuture, FutureExt}; +use libp2p_core::{transport::TransportError, Multiaddr, Transport}; +use tor_rtcompat::PreferredRuntime; + +mod address; + +#[cfg(feature = "async-std")] +pub mod async_io; +#[cfg(feature = "async-std")] +pub use crate::async_io::OnionStream; + +#[cfg(feature = "tokio")] +pub mod tokio; +#[cfg(feature = "tokio")] +pub use crate::tokio::OnionStream; + +#[derive(Debug, thiserror::Error)] +pub enum OnionError { + #[error("error during address translation")] + AddrErr(#[from] TorAddrError), + #[error("error in arti")] + ArtiErr(#[from] arti_client::Error), + #[error("onion services are not implented yet, since arti doesn't support it. (awaiting Arti 1.2.0)")] + OnionServiceUnimplemented, +} + +pub struct OnionClient { + client: Arc>, +} + +pub type OnionBuilder = TorClientBuilder; + +impl OnionClient { + #[inline] + pub fn builder() -> OnionBuilder { + TorClient::builder() + } + + #[inline] + pub fn from_builder(builder: OnionBuilder) -> Result { + let client = Arc::new(builder.create_unbootstrapped()?); + Ok(Self { client }) + } + + pub async fn bootstrap(&self) -> Result<(), OnionError> { + Ok(self.client.bootstrap().await?) + } +} + +#[derive(Debug, Clone, Copy, Default)] +pub struct AlwaysErrorListenerUpgrade; + +impl core::future::Future for AlwaysErrorListenerUpgrade { + type Output = Result; + fn poll( + self: std::pin::Pin<&mut Self>, + _cx: &mut std::task::Context<'_>, + ) -> std::task::Poll { + core::task::Poll::Ready(Err(OnionError::OnionServiceUnimplemented)) + } +} + +impl Transport for OnionClient { + type Output = OnionStream; + type Error = OnionError; + type Dial = BoxFuture<'static, Result>; + type ListenerUpgrade = AlwaysErrorListenerUpgrade; + + fn listen_on( + &mut self, + _addr: libp2p_core::Multiaddr, + ) -> Result< + libp2p_core::transport::ListenerId, + libp2p_core::transport::TransportError, + > { + Err(TransportError::Other(OnionError::OnionServiceUnimplemented)) + } + + fn remove_listener(&mut self, _id: libp2p_core::transport::ListenerId) -> bool { + false + } + + fn dial(&mut self, addr: Multiaddr) -> Result> { + let tor_address = dangerous_extract_tor_address(&addr) + .map_err(OnionError::from) + .map_err(TransportError::Other)?; + let onion_client = self.client.clone(); + Ok(async move { Ok(OnionStream::new(onion_client.connect(tor_address).await?)) }.boxed()) + } + + fn dial_as_listener( + &mut self, + addr: Multiaddr, + ) -> Result> { + self.dial(addr) + } + + fn address_translation(&self, _listen: &Multiaddr, _observed: &Multiaddr) -> Option { + None + } + + fn poll( + self: Pin<&mut Self>, + _cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> + { + std::task::Poll::Pending + } +} diff --git a/transports/onion/src/tokio.rs b/transports/onion/src/tokio.rs new file mode 100644 index 00000000000..892de085709 --- /dev/null +++ b/transports/onion/src/tokio.rs @@ -0,0 +1,67 @@ +use std::pin::Pin; + +use arti_client::DataStream; +use futures::{AsyncRead, AsyncWrite}; + +pub struct OnionStream { + inner: DataStream, +} + +impl OnionStream { + #[inline] + pub(super) fn new(inner: DataStream) -> Self { + Self { inner } + } +} + +impl AsyncRead for OnionStream { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + let mut read_buf = tokio_crate::io::ReadBuf::new(buf); + futures::ready!(tokio_crate::io::AsyncRead::poll_read( + Pin::new(&mut self.inner), + cx, + &mut read_buf + ))?; + std::task::Poll::Ready(Ok(read_buf.filled().len())) + } +} + +impl AsyncWrite for OnionStream { + #[inline] + fn poll_write( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + tokio_crate::io::AsyncWrite::poll_write(Pin::new(&mut self.inner), cx, buf) + } + + #[inline] + fn poll_flush( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + tokio_crate::io::AsyncWrite::poll_flush(Pin::new(&mut self.inner), cx) + } + + #[inline] + fn poll_close( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + tokio_crate::io::AsyncWrite::poll_shutdown(Pin::new(&mut self.inner), cx) + } + + #[inline] + fn poll_write_vectored( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + bufs: &[std::io::IoSlice<'_>], + ) -> std::task::Poll> { + tokio_crate::io::AsyncWrite::poll_write_vectored(Pin::new(&mut self.inner), cx, bufs) + } +} diff --git a/transports/onion/tests/mod.rs b/transports/onion/tests/mod.rs new file mode 100644 index 00000000000..c5a01ec1c61 --- /dev/null +++ b/transports/onion/tests/mod.rs @@ -0,0 +1,26 @@ +use std::error::Error; + +use libp2p::*; +use libp2p_onion::OnionClient; + +#[cfg(feature = "async-std")] +use async_std_crate as async_std; + +#[cfg(feature ="async-std")] +#[async_std_crate::test] +async fn onion_transport(keypair: identity::Keypair) -> Result<(), Box> { + let transport = { + let dns_tcp = dns::DnsConfig::system(tcp::TcpTransport::new( + tcp::GenTcpConfig::new().nodelay(true), + )) + .await?; + let onion = OnionClient::from_builder(OnionClient::builder())?; + onion.bootstrap().await?; + dns_tcp.or_transport(onion) + }; + + let noise_keys = noise::Keypair::::new() + .expect("Signing libp2p-noise static DH keypair failed."); + + Ok(()) +} From ca954bf516f9036e88f64e0e1961d92580789d87 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 13 Sep 2022 22:52:31 +0200 Subject: [PATCH 02/47] Added tests, comments and mate implementation somewhat usesable --- transports/onion/Cargo.toml | 3 ++ transports/onion/src/lib.rs | 30 +++++++++++--- transports/onion/tests/mod.rs | 74 ++++++++++++++++++++++++++++++++--- 3 files changed, 95 insertions(+), 12 deletions(-) diff --git a/transports/onion/Cargo.toml b/transports/onion/Cargo.toml index 33ee5cd61b7..1d51c7f510b 100644 --- a/transports/onion/Cargo.toml +++ b/transports/onion/Cargo.toml @@ -28,3 +28,6 @@ async-std = ["arti-client/async-std"] native-tls = ["arti-client/native-tls"] rustls = ["arti-client/rustls"] +[package.metadata.docs.rs] +rustdoc-args = ["--cfg", "docsrs"] +rustc-args = ["--cfg", "docsrs"] diff --git a/transports/onion/src/lib.rs b/transports/onion/src/lib.rs index e4052cd7b1e..f4417b144e2 100644 --- a/transports/onion/src/lib.rs +++ b/transports/onion/src/lib.rs @@ -1,3 +1,5 @@ +#![cfg_attr(docsrs, feature(doc_cfg))] + #[cfg(all(feature = "async-std", feature = "tokio"))] compile_error!("The features `async-std` and `tokio` are mutually exclusive"); @@ -20,14 +22,19 @@ use tor_rtcompat::PreferredRuntime; mod address; #[cfg(feature = "async-std")] -pub mod async_io; +mod async_io; #[cfg(feature = "async-std")] -pub use crate::async_io::OnionStream; +#[doc(hidden)] +pub use crate::async_io::OnionStream as PrivateOnionStream; #[cfg(feature = "tokio")] -pub mod tokio; +mod tokio; #[cfg(feature = "tokio")] -pub use crate::tokio::OnionStream; +#[doc(hidden)] +pub use crate::tokio::OnionStream as PrivateOnionStream; + +#[cfg(any(feature = "tokio", feature = "async-std", docsrs))] +pub use PrivateOnionStream as OnionStream; #[derive(Debug, thiserror::Error)] pub enum OnionError { @@ -40,6 +47,9 @@ pub enum OnionError { } pub struct OnionClient { + // client is in an Arc, because wihtout it the Transport::Dial method can't be implemented, + // due to lifetime issues. With the, eventual, stabilization of static async traits this issue + // will be resolved. client: Arc>, } @@ -81,16 +91,20 @@ impl Transport for OnionClient { type Dial = BoxFuture<'static, Result>; type ListenerUpgrade = AlwaysErrorListenerUpgrade; + /// Always returns `TransportError::MultiaddrNotSupported` fn listen_on( &mut self, - _addr: libp2p_core::Multiaddr, + addr: libp2p_core::Multiaddr, ) -> Result< libp2p_core::transport::ListenerId, libp2p_core::transport::TransportError, > { - Err(TransportError::Other(OnionError::OnionServiceUnimplemented)) + // although this address might be supported, this is returned in order to not provoke an + // error when trying to listen on this transport. + Err(TransportError::MultiaddrNotSupported(addr)) } + /// Always returns false fn remove_listener(&mut self, _id: libp2p_core::transport::ListenerId) -> bool { false } @@ -103,6 +117,7 @@ impl Transport for OnionClient { Ok(async move { Ok(OnionStream::new(onion_client.connect(tor_address).await?)) }.boxed()) } + /// Equivalent to `Transport::dial` fn dial_as_listener( &mut self, addr: Multiaddr, @@ -110,15 +125,18 @@ impl Transport for OnionClient { self.dial(addr) } + /// always returns `None` fn address_translation(&self, _listen: &Multiaddr, _observed: &Multiaddr) -> Option { None } + /// always returns pending fn poll( self: Pin<&mut Self>, _cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { + // pending is returned here, because this won't panic an OrTransport. std::task::Poll::Pending } } diff --git a/transports/onion/tests/mod.rs b/transports/onion/tests/mod.rs index c5a01ec1c61..61a283d1ce6 100644 --- a/transports/onion/tests/mod.rs +++ b/transports/onion/tests/mod.rs @@ -1,26 +1,88 @@ use std::error::Error; -use libp2p::*; +use libp2p::{ + dns, identity, mplex, noise, + ping::{self, Behaviour}, + swarm::SwarmEvent, + tcp, yamux, PeerId, Swarm, Transport, +}; use libp2p_onion::OnionClient; #[cfg(feature = "async-std")] use async_std_crate as async_std; -#[cfg(feature ="async-std")] -#[async_std_crate::test] -async fn onion_transport(keypair: identity::Keypair) -> Result<(), Box> { +// a sample onion transport setup +#[allow(unused)] +#[cfg(feature = "async-std")] +async fn onion_transport( + keypair: identity::Keypair, +) -> Result< + libp2p_core::transport::Boxed<(PeerId, libp2p_core::muxing::StreamMuxerBox)>, + Box, +> { + use std::time::Duration; + + use libp2p_core::upgrade::{SelectUpgrade, Version}; + let transport = { let dns_tcp = dns::DnsConfig::system(tcp::TcpTransport::new( tcp::GenTcpConfig::new().nodelay(true), )) .await?; let onion = OnionClient::from_builder(OnionClient::builder())?; + println!("bootstrapping..."); onion.bootstrap().await?; - dns_tcp.or_transport(onion) + println!("bootstrapped!"); + onion.or_transport(dns_tcp) }; let noise_keys = noise::Keypair::::new() + .into_authentic(&keypair) .expect("Signing libp2p-noise static DH keypair failed."); + Ok(transport + .upgrade(Version::V1) + .authenticate(noise::NoiseConfig::xx(noise_keys).into_authenticated()) + .multiplex(SelectUpgrade::new( + yamux::YamuxConfig::default(), + mplex::MplexConfig::default(), + )) + .timeout(Duration::from_secs(20)) + .boxed()) +} + +#[cfg(feature = "async-std")] +async fn onion_ping_swarm(keypair: identity::Keypair) -> Result, Box> { + let local_peer_id = PeerId::from(keypair.public()); + let transport = onion_transport(keypair).await?; + + let behaviour = ping::Behaviour::new(ping::Config::new().with_keep_alive(true)); + + let swarm = Swarm::new(transport, behaviour, local_peer_id); + Ok(swarm) +} + +#[cfg(feature = "async-std")] +#[async_std_crate::test] +async fn try_listen() { + let local_key = identity::Keypair::generate_ed25519(); + + let mut swarm = onion_ping_swarm(local_key) + .await + .expect("error while creating swarm"); + + swarm + .listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap()) + .expect("listen on failed"); + + use futures::prelude::*; - Ok(()) + loop { + match swarm.select_next_some().await { + SwarmEvent::NewListenAddr { address, .. } => { + println!("Listening on {:?}", address); + break; + } + _ => {} + } + } } From 59e47927ae551b4d9cd30520ddf5adb3f2432755 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 13 Sep 2022 23:45:07 +0200 Subject: [PATCH 03/47] simplified, streamlined and added an example --- transports/onion/src/async_io.rs | 71 ----------------- transports/onion/src/lib.rs | 31 ++------ transports/onion/src/provider.rs | 131 +++++++++++++++++++++++++++++++ transports/onion/src/tokio.rs | 67 ---------------- 4 files changed, 137 insertions(+), 163 deletions(-) delete mode 100644 transports/onion/src/async_io.rs create mode 100644 transports/onion/src/provider.rs delete mode 100644 transports/onion/src/tokio.rs diff --git a/transports/onion/src/async_io.rs b/transports/onion/src/async_io.rs deleted file mode 100644 index 99889bdd841..00000000000 --- a/transports/onion/src/async_io.rs +++ /dev/null @@ -1,71 +0,0 @@ -use std::pin::Pin; - -use arti_client::DataStream; -use futures::{AsyncRead, AsyncWrite}; - -pub struct OnionStream { - inner: DataStream, -} - -impl OnionStream { - #[inline] - pub(super) fn new(inner: DataStream) -> Self { - Self { inner } - } -} - -impl AsyncRead for OnionStream { - #[inline] - fn poll_read( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &mut [u8], - ) -> std::task::Poll> { - Pin::new(&mut self.inner).poll_read(cx, buf) - } - - #[inline] - fn poll_read_vectored( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - bufs: &mut [std::io::IoSliceMut<'_>], - ) -> std::task::Poll> { - Pin::new(&mut self.inner).poll_read_vectored(cx, bufs) - } -} - -impl AsyncWrite for OnionStream { - #[inline] - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> std::task::Poll> { - Pin::new(&mut self.inner).poll_write(cx, buf) - } - - #[inline] - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - Pin::new(&mut self.inner).poll_flush(cx) - } - - #[inline] - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - Pin::new(&mut self.inner).poll_close(cx) - } - - #[inline] - fn poll_write_vectored( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - bufs: &[std::io::IoSlice<'_>], - ) -> std::task::Poll> { - Pin::new(&mut self).poll_write_vectored(cx, bufs) - } -} diff --git a/transports/onion/src/lib.rs b/transports/onion/src/lib.rs index f4417b144e2..a303a76dc53 100644 --- a/transports/onion/src/lib.rs +++ b/transports/onion/src/lib.rs @@ -1,16 +1,7 @@ -#![cfg_attr(docsrs, feature(doc_cfg))] +#![doc(html_logo_url = "https://libp2p.io/img/logo_small.png")] +#![doc(html_favicon_url = "https://libp2p.io/img/favicon.png")] -#[cfg(all(feature = "async-std", feature = "tokio"))] -compile_error!("The features `async-std` and `tokio` are mutually exclusive"); - -// this is somewhat unnecessary, since arti-client won't compile before this is evaluated. -#[cfg(not(any(feature = "async-std", feature = "tokio")))] -compile_error!("Either one of the features `async-std` or `tokio` have to be enabled"); - -#[cfg(all(feature = "native-tls", feature = "rustls"))] -compile_error!("The features `native-tls` and `tokio` are mutually exclusive"); - -use core::pin::Pin; +use std::pin::Pin; use std::sync::Arc; use address::dangerous_extract_tor_address; @@ -20,21 +11,11 @@ use libp2p_core::{transport::TransportError, Multiaddr, Transport}; use tor_rtcompat::PreferredRuntime; mod address; +mod provider; -#[cfg(feature = "async-std")] -mod async_io; -#[cfg(feature = "async-std")] -#[doc(hidden)] -pub use crate::async_io::OnionStream as PrivateOnionStream; - -#[cfg(feature = "tokio")] -mod tokio; -#[cfg(feature = "tokio")] -#[doc(hidden)] -pub use crate::tokio::OnionStream as PrivateOnionStream; +#[doc(inline)] +pub use provider::OnionStream; -#[cfg(any(feature = "tokio", feature = "async-std", docsrs))] -pub use PrivateOnionStream as OnionStream; #[derive(Debug, thiserror::Error)] pub enum OnionError { diff --git a/transports/onion/src/provider.rs b/transports/onion/src/provider.rs new file mode 100644 index 00000000000..8bc74dd5cc5 --- /dev/null +++ b/transports/onion/src/provider.rs @@ -0,0 +1,131 @@ +use std::pin::Pin; + +use arti_client::DataStream; +use futures::{AsyncRead, AsyncWrite}; + +pub struct OnionStream { + inner: DataStream, +} + +impl OnionStream { + #[inline] + pub(super) fn new(inner: DataStream) -> Self { + Self { inner } + } +} + +#[cfg(feature = "tokio")] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +impl AsyncRead for OnionStream { + fn poll_read( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + let mut read_buf = tokio_crate::io::ReadBuf::new(buf); + futures::ready!(tokio_crate::io::AsyncRead::poll_read( + Pin::new(&mut self.inner), + cx, + &mut read_buf + ))?; + std::task::Poll::Ready(Ok(read_buf.filled().len())) + } +} + +#[cfg(feature = "tokio")] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +impl AsyncWrite for OnionStream { + #[inline] + fn poll_write( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + tokio_crate::io::AsyncWrite::poll_write(Pin::new(&mut self.inner), cx, buf) + } + + #[inline] + fn poll_flush( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + tokio_crate::io::AsyncWrite::poll_flush(Pin::new(&mut self.inner), cx) + } + + #[inline] + fn poll_close( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + tokio_crate::io::AsyncWrite::poll_shutdown(Pin::new(&mut self.inner), cx) + } + + #[inline] + fn poll_write_vectored( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + bufs: &[std::io::IoSlice<'_>], + ) -> std::task::Poll> { + tokio_crate::io::AsyncWrite::poll_write_vectored(Pin::new(&mut self.inner), cx, bufs) + } +} + +#[cfg(all(feature = "async-std", not(feature = "tokio")))] +#[cfg_attr(docsrs, doc(cfg(feature = "async-std")))] +impl AsyncRead for OnionStream { + #[inline] + fn poll_read( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut [u8], + ) -> std::task::Poll> { + Pin::new(&mut self.inner).poll_read(cx, buf) + } + + #[inline] + fn poll_read_vectored( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + bufs: &mut [std::io::IoSliceMut<'_>], + ) -> std::task::Poll> { + Pin::new(&mut self.inner).poll_read_vectored(cx, bufs) + } +} + +#[cfg(all(feature = "async-std", not(feature = "tokio")))] +#[cfg_attr(docsrs, doc(cfg(feature = "async-std")))] +impl AsyncWrite for OnionStream { + #[inline] + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + Pin::new(&mut self.inner).poll_write(cx, buf) + } + + #[inline] + fn poll_flush( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + Pin::new(&mut self.inner).poll_flush(cx) + } + + #[inline] + fn poll_close( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + Pin::new(&mut self.inner).poll_close(cx) + } + + #[inline] + fn poll_write_vectored( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + bufs: &[std::io::IoSlice<'_>], + ) -> std::task::Poll> { + Pin::new(&mut self).poll_write_vectored(cx, bufs) + } +} diff --git a/transports/onion/src/tokio.rs b/transports/onion/src/tokio.rs deleted file mode 100644 index 892de085709..00000000000 --- a/transports/onion/src/tokio.rs +++ /dev/null @@ -1,67 +0,0 @@ -use std::pin::Pin; - -use arti_client::DataStream; -use futures::{AsyncRead, AsyncWrite}; - -pub struct OnionStream { - inner: DataStream, -} - -impl OnionStream { - #[inline] - pub(super) fn new(inner: DataStream) -> Self { - Self { inner } - } -} - -impl AsyncRead for OnionStream { - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &mut [u8], - ) -> std::task::Poll> { - let mut read_buf = tokio_crate::io::ReadBuf::new(buf); - futures::ready!(tokio_crate::io::AsyncRead::poll_read( - Pin::new(&mut self.inner), - cx, - &mut read_buf - ))?; - std::task::Poll::Ready(Ok(read_buf.filled().len())) - } -} - -impl AsyncWrite for OnionStream { - #[inline] - fn poll_write( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> std::task::Poll> { - tokio_crate::io::AsyncWrite::poll_write(Pin::new(&mut self.inner), cx, buf) - } - - #[inline] - fn poll_flush( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - tokio_crate::io::AsyncWrite::poll_flush(Pin::new(&mut self.inner), cx) - } - - #[inline] - fn poll_close( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - tokio_crate::io::AsyncWrite::poll_shutdown(Pin::new(&mut self.inner), cx) - } - - #[inline] - fn poll_write_vectored( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - bufs: &[std::io::IoSlice<'_>], - ) -> std::task::Poll> { - tokio_crate::io::AsyncWrite::poll_write_vectored(Pin::new(&mut self.inner), cx, bufs) - } -} From f69a566ebd02c2294840e7b8bef0ffdfe3184a24 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 13 Sep 2022 23:45:21 +0200 Subject: [PATCH 04/47] simplified, streamlined and added an example --- Cargo.toml | 13 +++++ examples/ping-onion.rs | 119 +++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 4 ++ 3 files changed, 136 insertions(+) create mode 100644 examples/ping-onion.rs diff --git a/Cargo.toml b/Cargo.toml index 1cafbb1482f..3f39ffa8aac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,9 @@ default = [ "wasm-ext", "websocket", "yamux", + "onion", + "onion-async-std", + "onion-native-tls" ] autonat = ["dep:libp2p-autonat"] @@ -68,6 +71,11 @@ yamux = ["dep:libp2p-yamux"] secp256k1 = ["libp2p-core/secp256k1"] rsa = ["libp2p-core/rsa"] serde = ["libp2p-core/serde", "libp2p-kad?/serde", "libp2p-gossipsub?/serde"] +onion = ["dep:libp2p-onion"] +onion-async-std = ["libp2p-onion?/async-std"] +onion-tokio = ["libp2p-onion?/tokio"] +onion-native-tls = ["libp2p-onion?/native-tls"] +onion-rustls = ["libp2p-onion?/rustls"] [package.metadata.docs.rs] all-features = true @@ -112,6 +120,7 @@ libp2p-dns = { version = "0.36.0", path = "transports/dns", optional = true, def libp2p-mdns = { version = "0.40.0", path = "protocols/mdns", optional = true, default-features = false } libp2p-tcp = { version = "0.37.0", path = "transports/tcp", default-features = false, optional = true } libp2p-websocket = { version = "0.38.0", path = "transports/websocket", optional = true } +libp2p-onion = { version = "0.1.0", path = "transports/onion", default-features = false, optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] libp2p-gossipsub = { version = "0.41.0", path = "protocols/gossipsub", optional = true } @@ -177,3 +186,7 @@ required-features = ["gossipsub"] [[example]] name = "ipfs-private" required-features = ["gossipsub"] + +[[example]] +name = "ping-onion" +required-features = ["onion", "onion-async-std", "onion-native-tls", "tcp-async-io", "dns-async-std"] diff --git a/examples/ping-onion.rs b/examples/ping-onion.rs new file mode 100644 index 00000000000..72676802db0 --- /dev/null +++ b/examples/ping-onion.rs @@ -0,0 +1,119 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! Ping example +//! +//! See ../src/tutorial.rs for a step-by-step guide building the example below. +//! +//! In the first terminal window, run: +//! +//! ```sh +//! cargo run --example ping +//! ``` +//! +//! It will print the PeerId and the listening addresses, e.g. `Listening on +//! "/ip4/0.0.0.0/tcp/24915"` +//! +//! In the second terminal window, start a new instance of the example with: +//! +//! ```sh +//! cargo run --example ping -- /ip4/127.0.0.1/tcp/24915 +//! ``` +//! +//! The two nodes establish a connection, negotiate the ping protocol +//! and begin pinging each other. + +use futures::prelude::*; +use libp2p::swarm::{Swarm, SwarmEvent}; +use libp2p::{identity, ping, Multiaddr, PeerId, dns, tcp, onion, Transport, noise, yamux, mplex, core::upgrade}; +use std::error::Error; + +async fn onion_transport( + keypair: identity::Keypair, +) -> Result< + libp2p_core::transport::Boxed<(PeerId, libp2p_core::muxing::StreamMuxerBox)>, + Box, +> { + use std::time::Duration; + + + let transport = { + let dns_tcp = dns::DnsConfig::system(tcp::TcpTransport::new( + tcp::GenTcpConfig::new().nodelay(true), + )) + .await?; + let onion = onion::OnionClient::from_builder(onion::OnionClient::builder())?; + println!("bootstrapping..."); + onion.bootstrap().await?; + println!("bootstrapped!"); + onion.or_transport(dns_tcp) + }; + + let noise_keys = noise::Keypair::::new() + .into_authentic(&keypair) + .expect("Signing libp2p-noise static DH keypair failed."); + Ok(transport + .upgrade(upgrade::Version::V1) + .authenticate(noise::NoiseConfig::xx(noise_keys).into_authenticated()) + .multiplex(upgrade::SelectUpgrade::new( + yamux::YamuxConfig::default(), + mplex::MplexConfig::default(), + )) + .timeout(Duration::from_secs(20)) + .boxed()) +} + +#[async_std::main] +async fn main() -> Result<(), Box> { + let local_key = identity::Keypair::generate_ed25519(); + let local_peer_id = PeerId::from(local_key.public()); + println!("Local peer id: {:?}", local_peer_id); + + let transport = onion_transport(local_key).await?; + + // Create a ping network behaviour. + // + // For illustrative purposes, the ping protocol is configured to + // keep the connection alive, so a continuous sequence of pings + // can be observed. + let behaviour = ping::Behaviour::new(ping::Config::new().with_keep_alive(true)); + + let mut swarm = Swarm::new(transport, behaviour, local_peer_id); + + // Tell the swarm to listen on all interfaces and a random, OS-assigned + // port. + swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?; + + // Dial the peer identified by the multi-address given as the second + // command-line argument, if any. + if let Some(addr) = std::env::args().nth(1) { + let remote: Multiaddr = addr.parse()?; + swarm.dial(remote)?; + println!("Dialed {}", addr) + } + + loop { + match swarm.select_next_some().await { + SwarmEvent::NewListenAddr { address, .. } => println!("Listening on {:?}", address), + SwarmEvent::Behaviour(event) => println!("{:?}", event), + _ => {} + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 3ed00408cb5..d65f0d5b44a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -147,6 +147,10 @@ pub use libp2p_websocket as websocket; #[cfg_attr(docsrs, doc(cfg(feature = "yamux")))] #[doc(inline)] pub use libp2p_yamux as yamux; +#[cfg(feature = "onion")] +#[cfg_attr(docsrs, doc(cfg(feature = "onion")))] +#[doc(inline)] +pub use libp2p_onion as onion; mod transport_ext; From 6cd8ce06d50b6ee0e0680bf2b32e74954aefd110 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 14 Sep 2022 00:23:36 +0200 Subject: [PATCH 05/47] Added some traits --- transports/onion/src/lib.rs | 1 + transports/onion/src/provider.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/transports/onion/src/lib.rs b/transports/onion/src/lib.rs index a303a76dc53..9027bf91312 100644 --- a/transports/onion/src/lib.rs +++ b/transports/onion/src/lib.rs @@ -27,6 +27,7 @@ pub enum OnionError { OnionServiceUnimplemented, } +#[derive(Clone)] pub struct OnionClient { // client is in an Arc, because wihtout it the Transport::Dial method can't be implemented, // due to lifetime issues. With the, eventual, stabilization of static async traits this issue diff --git a/transports/onion/src/provider.rs b/transports/onion/src/provider.rs index 8bc74dd5cc5..3685c4d70db 100644 --- a/transports/onion/src/provider.rs +++ b/transports/onion/src/provider.rs @@ -3,6 +3,7 @@ use std::pin::Pin; use arti_client::DataStream; use futures::{AsyncRead, AsyncWrite}; +#[derive(Debug)] pub struct OnionStream { inner: DataStream, } From ab0197e3fc9a9fbc35c6b27729527802f8eead11 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 14 Sep 2022 00:39:47 +0200 Subject: [PATCH 06/47] Added cargo fmt changes --- examples/ping-onion.rs | 6 ++++-- src/lib.rs | 8 ++++---- transports/onion/src/lib.rs | 1 - 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/examples/ping-onion.rs b/examples/ping-onion.rs index 72676802db0..890c81ef4e2 100644 --- a/examples/ping-onion.rs +++ b/examples/ping-onion.rs @@ -42,7 +42,10 @@ use futures::prelude::*; use libp2p::swarm::{Swarm, SwarmEvent}; -use libp2p::{identity, ping, Multiaddr, PeerId, dns, tcp, onion, Transport, noise, yamux, mplex, core::upgrade}; +use libp2p::{ + core::upgrade, dns, identity, mplex, noise, onion, ping, tcp, yamux, Multiaddr, PeerId, + Transport, +}; use std::error::Error; async fn onion_transport( @@ -53,7 +56,6 @@ async fn onion_transport( > { use std::time::Duration; - let transport = { let dns_tcp = dns::DnsConfig::system(tcp::TcpTransport::new( tcp::GenTcpConfig::new().nodelay(true), diff --git a/src/lib.rs b/src/lib.rs index d65f0d5b44a..53a26b0e786 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -99,6 +99,10 @@ pub use libp2p_mplex as mplex; #[cfg_attr(docsrs, doc(cfg(feature = "noise")))] #[doc(inline)] pub use libp2p_noise as noise; +#[cfg(feature = "onion")] +#[cfg_attr(docsrs, doc(cfg(feature = "onion")))] +#[doc(inline)] +pub use libp2p_onion as onion; #[cfg(feature = "ping")] #[cfg_attr(docsrs, doc(cfg(feature = "ping")))] #[doc(inline)] @@ -147,10 +151,6 @@ pub use libp2p_websocket as websocket; #[cfg_attr(docsrs, doc(cfg(feature = "yamux")))] #[doc(inline)] pub use libp2p_yamux as yamux; -#[cfg(feature = "onion")] -#[cfg_attr(docsrs, doc(cfg(feature = "onion")))] -#[doc(inline)] -pub use libp2p_onion as onion; mod transport_ext; diff --git a/transports/onion/src/lib.rs b/transports/onion/src/lib.rs index 9027bf91312..cfe724da95f 100644 --- a/transports/onion/src/lib.rs +++ b/transports/onion/src/lib.rs @@ -16,7 +16,6 @@ mod provider; #[doc(inline)] pub use provider::OnionStream; - #[derive(Debug, thiserror::Error)] pub enum OnionError { #[error("error during address translation")] From 0c31c371d146b33d53da07e545b87589d56f76dc Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 14 Sep 2022 01:10:57 +0200 Subject: [PATCH 07/47] fixed typo --- examples/ping-onion.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/ping-onion.rs b/examples/ping-onion.rs index 890c81ef4e2..5c66ebaf489 100644 --- a/examples/ping-onion.rs +++ b/examples/ping-onion.rs @@ -25,7 +25,7 @@ //! In the first terminal window, run: //! //! ```sh -//! cargo run --example ping +//! cargo run --example ping-onion //! ``` //! //! It will print the PeerId and the listening addresses, e.g. `Listening on @@ -34,7 +34,7 @@ //! In the second terminal window, start a new instance of the example with: //! //! ```sh -//! cargo run --example ping -- /ip4/127.0.0.1/tcp/24915 +//! cargo run --example ping-onion -- /ip4/127.0.0.1/tcp/24915 //! ``` //! //! The two nodes establish a connection, negotiate the ping protocol @@ -88,6 +88,7 @@ async fn main() -> Result<(), Box> { let local_peer_id = PeerId::from(local_key.public()); println!("Local peer id: {:?}", local_peer_id); + // create a transport let transport = onion_transport(local_key).await?; // Create a ping network behaviour. From 9380b18dd3716e218d6181b961a2638cff046ffb Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 14 Sep 2022 13:59:23 +0200 Subject: [PATCH 08/47] implemented suggestions --- transports/onion/src/address.rs | 52 ++++++++++++++- transports/onion/src/lib.rs | 110 ++++++++++++++++++++++++++------ transports/onion/tests/mod.rs | 88 ------------------------- 3 files changed, 139 insertions(+), 111 deletions(-) delete mode 100644 transports/onion/tests/mod.rs diff --git a/transports/onion/src/address.rs b/transports/onion/src/address.rs index 08d90236128..d3e72e043c1 100644 --- a/transports/onion/src/address.rs +++ b/transports/onion/src/address.rs @@ -1,13 +1,13 @@ use std::net::{IpAddr, SocketAddr}; -use arti_client::{TorAddr, TorAddrError}; +use arti_client::{IntoTorAddr, TorAddr, TorAddrError}; use libp2p_core::{multiaddr::Protocol, Multiaddr}; -fn try_extract_socket_addr(mutliaddr: &Multiaddr) -> Result { +fn try_extract_socket_addr(multiaddr: &Multiaddr) -> Result { let mut ip_4 = None; let mut ip_6 = None; let mut tcp_port_opt = None; - for e in mutliaddr.iter() { + for e in multiaddr.iter() { match e { Protocol::Ip4(a) => { ip_4 = Some(IpAddr::V4(a)); @@ -32,3 +32,49 @@ pub(super) fn dangerous_extract_tor_address( let socket_addr = try_extract_socket_addr(multiaddr)?; TorAddr::dangerously_from(socket_addr) } + +macro_rules! try_convert_to_tor_addr { + ($dns:ident, $tcp_port:ident, $tor_addr_error:ident) => { + if let Some(dns_s) = $dns { + match (dns_s.as_ref(), $tcp_port).into_tor_addr() { + Ok(tor_addr) => return Ok(tor_addr), + Err(e) => $tor_addr_error = Some(e), + } + } + }; +} + +pub(super) fn safe_extract_tor_address(multiaddr: &Multiaddr) -> Result { + let mut dns = None; + let mut dns_4 = None; + let mut dns_6 = None; + let mut dns_addr = None; + let mut tcp_port_opt = None; + for e in multiaddr.iter() { + match e { + Protocol::Dns(s) => { + dns = Some(s); + } + Protocol::Dns4(s) => { + dns_4 = Some(s); + } + Protocol::Dns6(s) => { + dns_6 = Some(s); + } + Protocol::Dnsaddr(s) => { + dns_addr = Some(s); + } + Protocol::Tcp(p) => { + tcp_port_opt = Some(p); + } + _ => {} + } + } + let tcp_port = tcp_port_opt.ok_or(TorAddrError::NoPort)?; + let mut tor_addr_error = None; + try_convert_to_tor_addr!(dns, tcp_port, tor_addr_error); + try_convert_to_tor_addr!(dns_4, tcp_port, tor_addr_error); + try_convert_to_tor_addr!(dns_6, tcp_port, tor_addr_error); + try_convert_to_tor_addr!(dns_addr, tcp_port, tor_addr_error); + Err(tor_addr_error.unwrap_or(TorAddrError::InvalidHostname)) +} diff --git a/transports/onion/src/lib.rs b/transports/onion/src/lib.rs index cfe724da95f..d6cb31ce277 100644 --- a/transports/onion/src/lib.rs +++ b/transports/onion/src/lib.rs @@ -1,14 +1,15 @@ #![doc(html_logo_url = "https://libp2p.io/img/logo_small.png")] #![doc(html_favicon_url = "https://libp2p.io/img/favicon.png")] +#![cfg_attr(docsrs, feature(doc_cfg))] use std::pin::Pin; use std::sync::Arc; -use address::dangerous_extract_tor_address; +use address::{dangerous_extract_tor_address, safe_extract_tor_address}; use arti_client::{TorAddrError, TorClient, TorClientBuilder}; use futures::{future::BoxFuture, FutureExt}; use libp2p_core::{transport::TransportError, Multiaddr, Transport}; -use tor_rtcompat::PreferredRuntime; +use tor_rtcompat::Runtime; mod address; mod provider; @@ -22,37 +23,95 @@ pub enum OnionError { AddrErr(#[from] TorAddrError), #[error("error in arti")] ArtiErr(#[from] arti_client::Error), - #[error("onion services are not implented yet, since arti doesn't support it. (awaiting Arti 1.2.0)")] - OnionServiceUnimplemented, } #[derive(Clone)] -pub struct OnionClient { +pub struct OnionClient { // client is in an Arc, because wihtout it the Transport::Dial method can't be implemented, // due to lifetime issues. With the, eventual, stabilization of static async traits this issue // will be resolved. - client: Arc>, + client: Arc>, + safe_mode: bool, } -pub type OnionBuilder = TorClientBuilder; +pub type OnionBuilder = TorClientBuilder; -impl OnionClient { +impl OnionClient { #[inline] - pub fn builder() -> OnionBuilder { - TorClient::builder() + pub fn from_builder(builder: OnionBuilder, safe_mode: bool) -> Result { + let client = Arc::new(builder.create_unbootstrapped()?); + Ok(Self { client, safe_mode }) } #[inline] - pub fn from_builder(builder: OnionBuilder) -> Result { - let client = Arc::new(builder.create_unbootstrapped()?); - Ok(Self { client }) + pub fn is_safe_mode(&self) -> bool { + self.safe_mode + } + + #[inline] + pub fn set_safe_mode(&mut self, mode: bool) { + self.safe_mode = mode } pub async fn bootstrap(&self) -> Result<(), OnionError> { - Ok(self.client.bootstrap().await?) + self.client.bootstrap().await.map_err(OnionError::ArtiErr) + } +} + +#[cfg(all(feature = "native-tls", feature = "async-std"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "native-tls", feature = "async-std"))))] +impl OnionClient { + pub fn builder() -> OnionBuilder { + let runtime = tor_rtcompat::async_std::AsyncStdNativeTlsRuntime::current() + .expect("Couldn't get the current async_std native-tls runtime"); + TorClient::with_runtime(runtime) + } +} + +#[cfg(all(feature = "rustls", feature = "async-std"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "rustls", feature = "async-std"))))] +impl OnionClient { + pub fn builder() -> OnionBuilder { + let runtime = tor_rtcompat::async_std::AsyncStdRustlsRuntime::current() + .expect("Couldn't get the current async_std rustls runtime"); + TorClient::with_runtime(runtime) + } +} + +#[cfg(all(feature = "native-tls", feature = "tokio"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "native-tls", feature = "tokio"))))] +impl OnionClient { + pub fn builder() -> OnionBuilder { + let runtime = tor_rtcompat::tokio::TokioNativeTlsRuntime::current() + .expect("Couldn't get the current tokio native-tls runtime"); + TorClient::with_runtime(runtime) } } +#[cfg(all(feature = "rustls", feature = "tokio"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "rustls", feature = "tokio"))))] +impl OnionClient { + pub fn builder() -> OnionBuilder { + let runtime = tor_rtcompat::tokio::TokioRustlsRuntime::current() + .expect("Couldn't get the current tokio rustls runtime"); + TorClient::with_runtime(runtime) + } +} + +#[cfg(all(feature = "native-tls", feature = "async-std"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "native-tls", feature = "async-std"))))] +pub type OnionAsyncStdNativeTlsClient = + OnionClient; +#[cfg(all(feature = "rustls", feature = "async-std"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "rustls", feature = "async-std"))))] +pub type OnionAsyncStdRustlsClient = OnionClient; +#[cfg(all(feature = "native-tls", feature = "tokio"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "native-tls", feature = "tokio"))))] +pub type OnionTokioNativeTlsClient = OnionClient; +#[cfg(all(feature = "rustls", feature = "tokio"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "rustls", feature = "tokio"))))] +pub type OnionTokioRustlsClient = OnionClient; + #[derive(Debug, Clone, Copy, Default)] pub struct AlwaysErrorListenerUpgrade; @@ -62,11 +121,11 @@ impl core::future::Future for AlwaysErrorListenerUpgrade { self: std::pin::Pin<&mut Self>, _cx: &mut std::task::Context<'_>, ) -> std::task::Poll { - core::task::Poll::Ready(Err(OnionError::OnionServiceUnimplemented)) + panic!("onion services are not implented yet, since arti doesn't support it. (awaiting Arti 1.2.0)") } } -impl Transport for OnionClient { +impl Transport for OnionClient { type Output = OnionStream; type Error = OnionError; type Dial = BoxFuture<'static, Result>; @@ -91,11 +150,22 @@ impl Transport for OnionClient { } fn dial(&mut self, addr: Multiaddr) -> Result> { - let tor_address = dangerous_extract_tor_address(&addr) - .map_err(OnionError::from) - .map_err(TransportError::Other)?; + let tor_address = if !self.safe_mode { + safe_extract_tor_address(&addr).or_else(|_| dangerous_extract_tor_address(&addr)) + } else { + safe_extract_tor_address(&addr) + } + .map_err(OnionError::from) + .map_err(TransportError::Other)?; let onion_client = self.client.clone(); - Ok(async move { Ok(OnionStream::new(onion_client.connect(tor_address).await?)) }.boxed()) + Ok(async move { + onion_client + .connect(tor_address) + .await + .map(OnionStream::new) + .map_err(OnionError::ArtiErr) + } + .boxed()) } /// Equivalent to `Transport::dial` diff --git a/transports/onion/tests/mod.rs b/transports/onion/tests/mod.rs deleted file mode 100644 index 61a283d1ce6..00000000000 --- a/transports/onion/tests/mod.rs +++ /dev/null @@ -1,88 +0,0 @@ -use std::error::Error; - -use libp2p::{ - dns, identity, mplex, noise, - ping::{self, Behaviour}, - swarm::SwarmEvent, - tcp, yamux, PeerId, Swarm, Transport, -}; -use libp2p_onion::OnionClient; - -#[cfg(feature = "async-std")] -use async_std_crate as async_std; - -// a sample onion transport setup -#[allow(unused)] -#[cfg(feature = "async-std")] -async fn onion_transport( - keypair: identity::Keypair, -) -> Result< - libp2p_core::transport::Boxed<(PeerId, libp2p_core::muxing::StreamMuxerBox)>, - Box, -> { - use std::time::Duration; - - use libp2p_core::upgrade::{SelectUpgrade, Version}; - - let transport = { - let dns_tcp = dns::DnsConfig::system(tcp::TcpTransport::new( - tcp::GenTcpConfig::new().nodelay(true), - )) - .await?; - let onion = OnionClient::from_builder(OnionClient::builder())?; - println!("bootstrapping..."); - onion.bootstrap().await?; - println!("bootstrapped!"); - onion.or_transport(dns_tcp) - }; - - let noise_keys = noise::Keypair::::new() - .into_authentic(&keypair) - .expect("Signing libp2p-noise static DH keypair failed."); - Ok(transport - .upgrade(Version::V1) - .authenticate(noise::NoiseConfig::xx(noise_keys).into_authenticated()) - .multiplex(SelectUpgrade::new( - yamux::YamuxConfig::default(), - mplex::MplexConfig::default(), - )) - .timeout(Duration::from_secs(20)) - .boxed()) -} - -#[cfg(feature = "async-std")] -async fn onion_ping_swarm(keypair: identity::Keypair) -> Result, Box> { - let local_peer_id = PeerId::from(keypair.public()); - let transport = onion_transport(keypair).await?; - - let behaviour = ping::Behaviour::new(ping::Config::new().with_keep_alive(true)); - - let swarm = Swarm::new(transport, behaviour, local_peer_id); - Ok(swarm) -} - -#[cfg(feature = "async-std")] -#[async_std_crate::test] -async fn try_listen() { - let local_key = identity::Keypair::generate_ed25519(); - - let mut swarm = onion_ping_swarm(local_key) - .await - .expect("error while creating swarm"); - - swarm - .listen_on("/ip4/0.0.0.0/tcp/0".parse().unwrap()) - .expect("listen on failed"); - - use futures::prelude::*; - - loop { - match swarm.select_next_some().await { - SwarmEvent::NewListenAddr { address, .. } => { - println!("Listening on {:?}", address); - break; - } - _ => {} - } - } -} From 3dabad1d643777b97fe924fa83cad4492a376166 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 14 Sep 2022 13:59:39 +0200 Subject: [PATCH 09/47] implemented suggestions --- examples/ping-onion.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ping-onion.rs b/examples/ping-onion.rs index 5c66ebaf489..c9ba048589c 100644 --- a/examples/ping-onion.rs +++ b/examples/ping-onion.rs @@ -61,7 +61,7 @@ async fn onion_transport( tcp::GenTcpConfig::new().nodelay(true), )) .await?; - let onion = onion::OnionClient::from_builder(onion::OnionClient::builder())?; + let onion = onion::OnionClient::from_builder(onion::OnionClient::builder(), false)?; println!("bootstrapping..."); onion.bootstrap().await?; println!("bootstrapped!"); From 0c7298c9927f76736847383479a961138352b60f Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 19 Sep 2022 16:48:51 +0200 Subject: [PATCH 10/47] Addressed latest issues --- Cargo.toml | 10 +- examples/ping-onion.rs | 25 ++--- transports/onion/src/address.rs | 161 +++++++++++++++++++------------- transports/onion/src/lib.rs | 64 ++++++++----- 4 files changed, 150 insertions(+), 110 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3f39ffa8aac..91dd8c94417 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,6 @@ default = [ "wasm-ext", "websocket", "yamux", - "onion", "onion-async-std", "onion-native-tls" ] @@ -71,11 +70,10 @@ yamux = ["dep:libp2p-yamux"] secp256k1 = ["libp2p-core/secp256k1"] rsa = ["libp2p-core/rsa"] serde = ["libp2p-core/serde", "libp2p-kad?/serde", "libp2p-gossipsub?/serde"] -onion = ["dep:libp2p-onion"] -onion-async-std = ["libp2p-onion?/async-std"] -onion-tokio = ["libp2p-onion?/tokio"] -onion-native-tls = ["libp2p-onion?/native-tls"] -onion-rustls = ["libp2p-onion?/rustls"] +onion-async-std = ["libp2p-onion/async-std"] +onion-tokio = ["libp2p-onion/tokio"] +onion-native-tls = ["libp2p-onion/native-tls"] +onion-rustls = ["libp2p-onion/rustls"] [package.metadata.docs.rs] all-features = true diff --git a/examples/ping-onion.rs b/examples/ping-onion.rs index c9ba048589c..1b0a515854a 100644 --- a/examples/ping-onion.rs +++ b/examples/ping-onion.rs @@ -57,15 +57,11 @@ async fn onion_transport( use std::time::Duration; let transport = { - let dns_tcp = dns::DnsConfig::system(tcp::TcpTransport::new( - tcp::GenTcpConfig::new().nodelay(true), - )) - .await?; let onion = onion::OnionClient::from_builder(onion::OnionClient::builder(), false)?; println!("bootstrapping..."); onion.bootstrap().await?; println!("bootstrapped!"); - onion.or_transport(dns_tcp) + onion }; let noise_keys = noise::Keypair::::new() @@ -84,6 +80,7 @@ async fn onion_transport( #[async_std::main] async fn main() -> Result<(), Box> { + let addr = std::env::args().nth(1).expect("no multiaddr given"); let local_key = identity::Keypair::generate_ed25519(); let local_peer_id = PeerId::from(local_key.public()); println!("Local peer id: {:?}", local_peer_id); @@ -100,21 +97,19 @@ async fn main() -> Result<(), Box> { let mut swarm = Swarm::new(transport, behaviour, local_peer_id); - // Tell the swarm to listen on all interfaces and a random, OS-assigned - // port. - swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?; - // Dial the peer identified by the multi-address given as the second // command-line argument, if any. - if let Some(addr) = std::env::args().nth(1) { - let remote: Multiaddr = addr.parse()?; - swarm.dial(remote)?; - println!("Dialed {}", addr) - } + let remote: Multiaddr = addr.parse()?; + swarm.dial(remote)?; + println!("Dialed {}", addr); loop { match swarm.select_next_some().await { - SwarmEvent::NewListenAddr { address, .. } => println!("Listening on {:?}", address), + SwarmEvent::ConnectionEstablished { endpoint, .. } => { + let endpoint_addr = endpoint.get_remote_address(); + println!("Connection established to {:?}", endpoint_addr); + }, + SwarmEvent::OutgoingConnectionError { error, .. } => println!("Error establishing outgoing connection"), SwarmEvent::Behaviour(event) => println!("{:?}", event), _ => {} } diff --git a/transports/onion/src/address.rs b/transports/onion/src/address.rs index d3e72e043c1..eee172b3dcb 100644 --- a/transports/onion/src/address.rs +++ b/transports/onion/src/address.rs @@ -3,78 +3,111 @@ use std::net::{IpAddr, SocketAddr}; use arti_client::{IntoTorAddr, TorAddr, TorAddrError}; use libp2p_core::{multiaddr::Protocol, Multiaddr}; -fn try_extract_socket_addr(multiaddr: &Multiaddr) -> Result { - let mut ip_4 = None; - let mut ip_6 = None; - let mut tcp_port_opt = None; - for e in multiaddr.iter() { - match e { - Protocol::Ip4(a) => { - ip_4 = Some(IpAddr::V4(a)); - } - Protocol::Ip6(a) => { - ip_6 = Some(IpAddr::V6(a)); - } - Protocol::Tcp(p) => { - tcp_port_opt = Some(p); - } - _ => {} - } - } - let ip = ip_4.or(ip_6).ok_or(TorAddrError::InvalidHostname)?; - let tcp_port = tcp_port_opt.ok_or(TorAddrError::NoPort)?; - Ok(SocketAddr::new(ip, tcp_port)) +pub fn dangerous_extract_tor_address(multiaddr: &Multiaddr) -> Result { + let socket_addr = try_extract_socket_addr(&mut multiaddr.clone())?; + TorAddr::dangerously_from(socket_addr) } -pub(super) fn dangerous_extract_tor_address( - multiaddr: &Multiaddr, -) -> Result { - let socket_addr = try_extract_socket_addr(multiaddr)?; - TorAddr::dangerously_from(socket_addr) +pub fn safe_extract_tor_address(multiaddr: &mut Multiaddr) -> Result { + let dns_variant = multiaddr.pop().ok_or(TorAddrError::InvalidHostname)?; + let dns_addr = match dns_variant { + Protocol::Dns(dns) => dns, + Protocol::Dns4(dns) => dns, + Protocol::Dns6(dns) => dns, + _ => return Err(TorAddrError::InvalidHostname), + }; + let tcp_variant = multiaddr.pop().ok_or(TorAddrError::NoPort)?; + let tcp_port = match tcp_variant { + Protocol::Tcp(p) => p, + _ => return Err(TorAddrError::NoPort), + }; + let address_tuple = (dns_addr.as_ref(), tcp_port); + address_tuple.into_tor_addr() } -macro_rules! try_convert_to_tor_addr { - ($dns:ident, $tcp_port:ident, $tor_addr_error:ident) => { - if let Some(dns_s) = $dns { - match (dns_s.as_ref(), $tcp_port).into_tor_addr() { - Ok(tor_addr) => return Ok(tor_addr), - Err(e) => $tor_addr_error = Some(e), - } - } +fn try_extract_socket_addr(multiaddr: &mut Multiaddr) -> Result { + let ip_variant = multiaddr.pop().ok_or(TorAddrError::InvalidHostname)?; + let ip = match ip_variant { + Protocol::Ip4(ip4) => IpAddr::V4(ip4), + Protocol::Ip6(ip6) => IpAddr::V6(ip6), + _ => return Err(TorAddrError::InvalidHostname), + }; + let tcp_variant = multiaddr.pop().ok_or(TorAddrError::NoPort)?; + let port = match tcp_variant { + Protocol::Tcp(p) => p, + _ => return Err(TorAddrError::NoPort), }; + Ok(SocketAddr::new(ip, port)) } -pub(super) fn safe_extract_tor_address(multiaddr: &Multiaddr) -> Result { - let mut dns = None; - let mut dns_4 = None; - let mut dns_6 = None; - let mut dns_addr = None; - let mut tcp_port_opt = None; - for e in multiaddr.iter() { - match e { - Protocol::Dns(s) => { - dns = Some(s); - } - Protocol::Dns4(s) => { - dns_4 = Some(s); - } - Protocol::Dns6(s) => { - dns_6 = Some(s); - } - Protocol::Dnsaddr(s) => { - dns_addr = Some(s); - } - Protocol::Tcp(p) => { - tcp_port_opt = Some(p); +#[cfg(test)] +mod tests { + use std::{borrow::Cow, net::Ipv4Addr, ops::Not}; + + use libp2p_core::{multiaddr::multiaddr, Multiaddr}; + + use super::safe_extract_tor_address; + + #[test] + fn extract_correct_address() { + let dns_test_addr = multiaddr!(Dns(Cow::Borrowed("ip.tld")), Tcp(10u16)); + let dns_test_addr_res = safe_extract_tor_address(&mut dns_test_addr.clone()); + assert!(dns_test_addr_res.is_ok()); + assert!(dns_test_addr_res.unwrap().is_ip_address().not()); + + let dns_4_test_addr = multiaddr!(Dns4(Cow::Borrowed("dns.ip4.tld")), Tcp(11u16)); + let dns_4_test_addr_res = safe_extract_tor_address(&mut dns_4_test_addr.clone()); + assert!(dns_4_test_addr_res.is_ok()); + assert!(dns_4_test_addr_res.unwrap().is_ip_address().not()); + + let dns_6_test_addr = multiaddr!(Dns6(Cow::Borrowed("dns.ip6.tld")), Tcp(12u16)); + let dns_6_test_addr_res = safe_extract_tor_address(&mut dns_6_test_addr.clone()); + assert!(dns_6_test_addr_res.is_ok()); + assert!(dns_6_test_addr_res.unwrap().is_ip_address().not()); + + let addresses = [ + dns_test_addr, + dns_4_test_addr, + dns_6_test_addr, + Multiaddr::empty(), + ]; + + for (idx, a) in addresses.iter().enumerate() { + for (idy, b) in addresses.iter().enumerate() { + if idx == idy { + continue; + } + let mut new = Multiaddr::empty(); + a.iter().for_each(|e| { + new.push(e); + }); + b.iter().for_each(|e| { + new.push(e); + }); + let new_addr_res = safe_extract_tor_address(&mut new); + assert!(new_addr_res.is_ok()); } - _ => {} } } - let tcp_port = tcp_port_opt.ok_or(TorAddrError::NoPort)?; - let mut tor_addr_error = None; - try_convert_to_tor_addr!(dns, tcp_port, tor_addr_error); - try_convert_to_tor_addr!(dns_4, tcp_port, tor_addr_error); - try_convert_to_tor_addr!(dns_6, tcp_port, tor_addr_error); - try_convert_to_tor_addr!(dns_addr, tcp_port, tor_addr_error); - Err(tor_addr_error.unwrap_or(TorAddrError::InvalidHostname)) + + #[test] + fn detect_incorrect_address() { + let without_dns = multiaddr!(Tcp(10u16), Udp(12u16)); + let without_dns_res = safe_extract_tor_address(&mut without_dns.clone()); + assert_eq!( + without_dns_res, + Err(arti_client::TorAddrError::InvalidHostname) + ); + + let host = Cow::Borrowed("ip.tld"); + let without_port = multiaddr!(Dns(host.clone()), Dns4(host.clone()), Dns6(host.clone())); + let without_port_res = safe_extract_tor_address(&mut without_port.clone()); + assert_eq!(without_port_res, Err(arti_client::TorAddrError::NoPort)); + let with_ip_addr = multiaddr!(Tcp(10u16), Ip4("1.1.1.1".parse::().unwrap())); + let with_ip_addr_res = safe_extract_tor_address(&mut with_ip_addr.clone()); + assert_eq!( + with_ip_addr_res, + Err(arti_client::TorAddrError::InvalidHostname) + ); + } } diff --git a/transports/onion/src/lib.rs b/transports/onion/src/lib.rs index d6cb31ce277..cb3c54dacba 100644 --- a/transports/onion/src/lib.rs +++ b/transports/onion/src/lib.rs @@ -31,26 +31,30 @@ pub struct OnionClient { // due to lifetime issues. With the, eventual, stabilization of static async traits this issue // will be resolved. client: Arc>, - safe_mode: bool, + pub conversion_mode: AddressConversion, } pub type OnionBuilder = TorClientBuilder; +#[derive(Debug, Clone, Copy, Hash, Default, PartialEq, Eq, PartialOrd, Ord)] +pub enum AddressConversion { + /// uses only dns for address resolution + #[default] + DnsOnly, + /// uses ip and dns for addresses + IpAndDns, +} + impl OnionClient { - #[inline] - pub fn from_builder(builder: OnionBuilder, safe_mode: bool) -> Result { + pub fn from_builder( + builder: OnionBuilder, + conversion_mode: AddressConversion, + ) -> Result { let client = Arc::new(builder.create_unbootstrapped()?); - Ok(Self { client, safe_mode }) - } - - #[inline] - pub fn is_safe_mode(&self) -> bool { - self.safe_mode - } - - #[inline] - pub fn set_safe_mode(&mut self, mode: bool) { - self.safe_mode = mode + Ok(Self { + client, + conversion_mode, + }) } pub async fn bootstrap(&self) -> Result<(), OnionError> { @@ -58,6 +62,17 @@ impl OnionClient { } } +macro_rules! default_constructor { + () => { + pub async fn default() -> Result { + let builder = Self::builder(); + let ret = Self::from_builder(builder, AddressConversion::DnsOnly)?; + ret.bootstrap().await?; + Ok(ret) + } + }; +} + #[cfg(all(feature = "native-tls", feature = "async-std"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "native-tls", feature = "async-std"))))] impl OnionClient { @@ -66,6 +81,7 @@ impl OnionClient { .expect("Couldn't get the current async_std native-tls runtime"); TorClient::with_runtime(runtime) } + default_constructor!(); } #[cfg(all(feature = "rustls", feature = "async-std"))] @@ -76,6 +92,7 @@ impl OnionClient { .expect("Couldn't get the current async_std rustls runtime"); TorClient::with_runtime(runtime) } + default_constructor!(); } #[cfg(all(feature = "native-tls", feature = "tokio"))] @@ -86,6 +103,7 @@ impl OnionClient { .expect("Couldn't get the current tokio native-tls runtime"); TorClient::with_runtime(runtime) } + default_constructor!(); } #[cfg(all(feature = "rustls", feature = "tokio"))] @@ -96,6 +114,7 @@ impl OnionClient { .expect("Couldn't get the current tokio rustls runtime"); TorClient::with_runtime(runtime) } + default_constructor!(); } #[cfg(all(feature = "native-tls", feature = "async-std"))] @@ -144,19 +163,17 @@ impl Transport for OnionClient { Err(TransportError::MultiaddrNotSupported(addr)) } - /// Always returns false fn remove_listener(&mut self, _id: libp2p_core::transport::ListenerId) -> bool { false } - fn dial(&mut self, addr: Multiaddr) -> Result> { - let tor_address = if !self.safe_mode { - safe_extract_tor_address(&addr).or_else(|_| dangerous_extract_tor_address(&addr)) + fn dial(&mut self, mut addr: Multiaddr) -> Result> { + let tor_address = if self.conversion_mode == AddressConversion::IpAndDns { + safe_extract_tor_address(&mut addr).or_else(|_| dangerous_extract_tor_address(&addr)) } else { - safe_extract_tor_address(&addr) + safe_extract_tor_address(&mut addr) } - .map_err(OnionError::from) - .map_err(TransportError::Other)?; + .map_err(|_| TransportError::MultiaddrNotSupported(addr))?; let onion_client = self.client.clone(); Ok(async move { onion_client @@ -168,7 +185,6 @@ impl Transport for OnionClient { .boxed()) } - /// Equivalent to `Transport::dial` fn dial_as_listener( &mut self, addr: Multiaddr, @@ -176,18 +192,16 @@ impl Transport for OnionClient { self.dial(addr) } - /// always returns `None` fn address_translation(&self, _listen: &Multiaddr, _observed: &Multiaddr) -> Option { None } - /// always returns pending fn poll( self: Pin<&mut Self>, _cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - // pending is returned here, because this won't panic an OrTransport. + // pending is returned here because this transport doesn't support listening std::task::Poll::Pending } } From d88af2e615ada1447c20dd4740b02434dffdd3d3 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 19 Sep 2022 16:57:57 +0200 Subject: [PATCH 11/47] fixed test failure --- transports/onion/src/address.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/transports/onion/src/address.rs b/transports/onion/src/address.rs index eee172b3dcb..eb72486c361 100644 --- a/transports/onion/src/address.rs +++ b/transports/onion/src/address.rs @@ -9,6 +9,11 @@ pub fn dangerous_extract_tor_address(multiaddr: &Multiaddr) -> Result Result { + let tcp_variant = multiaddr.pop().ok_or(TorAddrError::NoPort)?; + let tcp_port = match tcp_variant { + Protocol::Tcp(p) => p, + _ => return Err(TorAddrError::NoPort), + }; let dns_variant = multiaddr.pop().ok_or(TorAddrError::InvalidHostname)?; let dns_addr = match dns_variant { Protocol::Dns(dns) => dns, @@ -16,27 +21,22 @@ pub fn safe_extract_tor_address(multiaddr: &mut Multiaddr) -> Result dns, _ => return Err(TorAddrError::InvalidHostname), }; - let tcp_variant = multiaddr.pop().ok_or(TorAddrError::NoPort)?; - let tcp_port = match tcp_variant { - Protocol::Tcp(p) => p, - _ => return Err(TorAddrError::NoPort), - }; let address_tuple = (dns_addr.as_ref(), tcp_port); address_tuple.into_tor_addr() } fn try_extract_socket_addr(multiaddr: &mut Multiaddr) -> Result { + let tcp_variant = multiaddr.pop().ok_or(TorAddrError::NoPort)?; + let port = match tcp_variant { + Protocol::Tcp(p) => p, + _ => return Err(TorAddrError::NoPort), + }; let ip_variant = multiaddr.pop().ok_or(TorAddrError::InvalidHostname)?; let ip = match ip_variant { Protocol::Ip4(ip4) => IpAddr::V4(ip4), Protocol::Ip6(ip6) => IpAddr::V6(ip6), _ => return Err(TorAddrError::InvalidHostname), }; - let tcp_variant = multiaddr.pop().ok_or(TorAddrError::NoPort)?; - let port = match tcp_variant { - Protocol::Tcp(p) => p, - _ => return Err(TorAddrError::NoPort), - }; Ok(SocketAddr::new(ip, port)) } @@ -96,7 +96,7 @@ mod tests { let without_dns_res = safe_extract_tor_address(&mut without_dns.clone()); assert_eq!( without_dns_res, - Err(arti_client::TorAddrError::InvalidHostname) + Err(arti_client::TorAddrError::NoPort) ); let host = Cow::Borrowed("ip.tld"); @@ -107,7 +107,7 @@ mod tests { let with_ip_addr_res = safe_extract_tor_address(&mut with_ip_addr.clone()); assert_eq!( with_ip_addr_res, - Err(arti_client::TorAddrError::InvalidHostname) + Err(arti_client::TorAddrError::NoPort) ); } } From 0680655fd7c42a9adfcd1be484407761437e7f5b Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 19 Sep 2022 16:59:15 +0200 Subject: [PATCH 12/47] Ran cargo fmt --- examples/ping-onion.rs | 8 +++++--- transports/onion/src/address.rs | 10 ++-------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/examples/ping-onion.rs b/examples/ping-onion.rs index 1b0a515854a..6bfd291cd93 100644 --- a/examples/ping-onion.rs +++ b/examples/ping-onion.rs @@ -105,11 +105,13 @@ async fn main() -> Result<(), Box> { loop { match swarm.select_next_some().await { - SwarmEvent::ConnectionEstablished { endpoint, .. } => { + SwarmEvent::ConnectionEstablished { endpoint, .. } => { let endpoint_addr = endpoint.get_remote_address(); println!("Connection established to {:?}", endpoint_addr); - }, - SwarmEvent::OutgoingConnectionError { error, .. } => println!("Error establishing outgoing connection"), + } + SwarmEvent::OutgoingConnectionError { error, .. } => { + println!("Error establishing outgoing connection") + } SwarmEvent::Behaviour(event) => println!("{:?}", event), _ => {} } diff --git a/transports/onion/src/address.rs b/transports/onion/src/address.rs index eb72486c361..353e84bddd1 100644 --- a/transports/onion/src/address.rs +++ b/transports/onion/src/address.rs @@ -94,10 +94,7 @@ mod tests { fn detect_incorrect_address() { let without_dns = multiaddr!(Tcp(10u16), Udp(12u16)); let without_dns_res = safe_extract_tor_address(&mut without_dns.clone()); - assert_eq!( - without_dns_res, - Err(arti_client::TorAddrError::NoPort) - ); + assert_eq!(without_dns_res, Err(arti_client::TorAddrError::NoPort)); let host = Cow::Borrowed("ip.tld"); let without_port = multiaddr!(Dns(host.clone()), Dns4(host.clone()), Dns6(host.clone())); @@ -105,9 +102,6 @@ mod tests { assert_eq!(without_port_res, Err(arti_client::TorAddrError::NoPort)); let with_ip_addr = multiaddr!(Tcp(10u16), Ip4("1.1.1.1".parse::().unwrap())); let with_ip_addr_res = safe_extract_tor_address(&mut with_ip_addr.clone()); - assert_eq!( - with_ip_addr_res, - Err(arti_client::TorAddrError::NoPort) - ); + assert_eq!(with_ip_addr_res, Err(arti_client::TorAddrError::NoPort)); } } From e04869a2457307953c14eeaaa8d5e14027a87619 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 20 Sep 2022 11:04:59 +0200 Subject: [PATCH 13/47] Implemented suggested changes --- Cargo.toml | 2 +- examples/ping-onion.rs | 37 ++++++-------- src/lib.rs | 2 +- transports/onion/src/address.rs | 74 ++++++++++------------------ transports/onion/src/lib.rs | 83 ++++++++++++++++--------------- transports/onion/src/provider.rs | 84 +++++++------------------------- 6 files changed, 106 insertions(+), 176 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 598aaab4308..8f2a966262d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -187,5 +187,5 @@ required-features = ["gossipsub", "pnet", "yamux", "ping", "noise", "tcp-async-i [[example]] name = "ping-onion" -required-features = ["onion", "onion-async-std", "onion-native-tls", "tcp-async-io", "dns-async-std"] +required-features = ["onion-async-std", "onion-native-tls"] diff --git a/examples/ping-onion.rs b/examples/ping-onion.rs index 6bfd291cd93..7675232aefc 100644 --- a/examples/ping-onion.rs +++ b/examples/ping-onion.rs @@ -1,4 +1,4 @@ -// Copyright 2018 Parity Technologies (UK) Ltd. +// Copyright 2022 Hannes Furmans // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), @@ -18,33 +18,35 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -//! Ping example +//! Ping-Onion example //! //! See ../src/tutorial.rs for a step-by-step guide building the example below. //! -//! In the first terminal window, run: +//! This example requires two seperate computers, one of which has to be reachable from the +//! internet. //! +//! On the first computer run: //! ```sh -//! cargo run --example ping-onion +//! cargo run --example ping //! ``` //! //! It will print the PeerId and the listening addresses, e.g. `Listening on //! "/ip4/0.0.0.0/tcp/24915"` //! -//! In the second terminal window, start a new instance of the example with: +//! Make sure that the first computer is reachable under one of these ip addresses and port. //! +//! On the second computer run: //! ```sh //! cargo run --example ping-onion -- /ip4/127.0.0.1/tcp/24915 //! ``` //! //! The two nodes establish a connection, negotiate the ping protocol -//! and begin pinging each other. +//! and begin pinging each other over Tor. use futures::prelude::*; use libp2p::swarm::{Swarm, SwarmEvent}; use libp2p::{ - core::upgrade, dns, identity, mplex, noise, onion, ping, tcp, yamux, Multiaddr, PeerId, - Transport, + core::upgrade, identity, mplex, noise, onion, ping, yamux, Multiaddr, PeerId, Transport, }; use std::error::Error; @@ -56,20 +58,13 @@ async fn onion_transport( > { use std::time::Duration; - let transport = { - let onion = onion::OnionClient::from_builder(onion::OnionClient::builder(), false)?; - println!("bootstrapping..."); - onion.bootstrap().await?; - println!("bootstrapped!"); - onion - }; - - let noise_keys = noise::Keypair::::new() - .into_authentic(&keypair) - .expect("Signing libp2p-noise static DH keypair failed."); + let transport = onion::OnionAsyncStdNativeTlsTransport::bootstrapped().await?; Ok(transport .upgrade(upgrade::Version::V1) - .authenticate(noise::NoiseConfig::xx(noise_keys).into_authenticated()) + .authenticate( + noise::NoiseAuthenticated::xx(&keypair) + .expect("Signing libp2p-noise static DH keypair failed."), + ) .multiplex(upgrade::SelectUpgrade::new( yamux::YamuxConfig::default(), mplex::MplexConfig::default(), @@ -110,7 +105,7 @@ async fn main() -> Result<(), Box> { println!("Connection established to {:?}", endpoint_addr); } SwarmEvent::OutgoingConnectionError { error, .. } => { - println!("Error establishing outgoing connection") + println!("Error establishing outgoing connection: {:?}", error) } SwarmEvent::Behaviour(event) => println!("{:?}", event), _ => {} diff --git a/src/lib.rs b/src/lib.rs index 52a6a88dae5..e9919958d31 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -99,7 +99,7 @@ pub use libp2p_mplex as mplex; #[cfg_attr(docsrs, doc(cfg(feature = "noise")))] #[doc(inline)] pub use libp2p_noise as noise; -#[cfg(feature = "onion")] +#[cfg(any(feature = "onion-async-std", feature = "onion-tokio"))] #[cfg_attr(docsrs, doc(cfg(feature = "onion")))] #[doc(inline)] pub use libp2p_onion as onion; diff --git a/transports/onion/src/address.rs b/transports/onion/src/address.rs index 353e84bddd1..d863180cf77 100644 --- a/transports/onion/src/address.rs +++ b/transports/onion/src/address.rs @@ -42,66 +42,42 @@ fn try_extract_socket_addr(multiaddr: &mut Multiaddr) -> Result().unwrap())); - let with_ip_addr_res = safe_extract_tor_address(&mut with_ip_addr.clone()); - assert_eq!(with_ip_addr_res, Err(arti_client::TorAddrError::NoPort)); + assert!( + all_correct, + "During the parsing of the faulty addresses, there was an incorrectness" + ); } } diff --git a/transports/onion/src/lib.rs b/transports/onion/src/lib.rs index cb3c54dacba..adae7ad1e91 100644 --- a/transports/onion/src/lib.rs +++ b/transports/onion/src/lib.rs @@ -2,11 +2,11 @@ #![doc(html_favicon_url = "https://libp2p.io/img/favicon.png")] #![cfg_attr(docsrs, feature(doc_cfg))] -use std::pin::Pin; use std::sync::Arc; +use std::{marker::PhantomData, pin::Pin}; use address::{dangerous_extract_tor_address, safe_extract_tor_address}; -use arti_client::{TorAddrError, TorClient, TorClientBuilder}; +use arti_client::{DataStream, TorClient, TorClientBuilder}; use futures::{future::BoxFuture, FutureExt}; use libp2p_core::{transport::TransportError, Multiaddr, Transport}; use tor_rtcompat::Runtime; @@ -14,28 +14,30 @@ use tor_rtcompat::Runtime; mod address; mod provider; +#[cfg(feature = "tokio")] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] #[doc(inline)] -pub use provider::OnionStream; - -#[derive(Debug, thiserror::Error)] -pub enum OnionError { - #[error("error during address translation")] - AddrErr(#[from] TorAddrError), - #[error("error in arti")] - ArtiErr(#[from] arti_client::Error), -} +pub use provider::OnionTokioStream; + +use provider::OnionStream; + +pub type OnionError = arti_client::Error; #[derive(Clone)] -pub struct OnionClient { +pub struct OnionTransport { // client is in an Arc, because wihtout it the Transport::Dial method can't be implemented, // due to lifetime issues. With the, eventual, stabilization of static async traits this issue // will be resolved. client: Arc>, + /// The used conversion mode to resolve addresses. One probably shouldn't access this directly. + /// The usage of [OnionTransport::with_address_conversion] at construction is recommended. pub conversion_mode: AddressConversion, + phantom: PhantomData, } pub type OnionBuilder = TorClientBuilder; +/// Mode of address conversion. Refer tor [arti_client::TorAddr](https://docs.rs/arti-client/latest/arti_client/struct.TorAddr.html) for details. #[derive(Debug, Clone, Copy, Hash, Default, PartialEq, Eq, PartialOrd, Ord)] pub enum AddressConversion { /// uses only dns for address resolution @@ -45,7 +47,7 @@ pub enum AddressConversion { IpAndDns, } -impl OnionClient { +impl OnionTransport { pub fn from_builder( builder: OnionBuilder, conversion_mode: AddressConversion, @@ -54,17 +56,23 @@ impl OnionClient { Ok(Self { client, conversion_mode, + phantom: PhantomData::default(), }) } pub async fn bootstrap(&self) -> Result<(), OnionError> { - self.client.bootstrap().await.map_err(OnionError::ArtiErr) + self.client.bootstrap().await + } + + pub fn with_address_conversion(&mut self, conversion_mode: AddressConversion) -> &mut Self { + self.conversion_mode = conversion_mode; + self } } macro_rules! default_constructor { () => { - pub async fn default() -> Result { + pub async fn bootstrapped() -> Result { let builder = Self::builder(); let ret = Self::from_builder(builder, AddressConversion::DnsOnly)?; ret.bootstrap().await?; @@ -75,7 +83,7 @@ macro_rules! default_constructor { #[cfg(all(feature = "native-tls", feature = "async-std"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "native-tls", feature = "async-std"))))] -impl OnionClient { +impl OnionTransport { pub fn builder() -> OnionBuilder { let runtime = tor_rtcompat::async_std::AsyncStdNativeTlsRuntime::current() .expect("Couldn't get the current async_std native-tls runtime"); @@ -86,7 +94,7 @@ impl OnionClient { #[cfg(all(feature = "rustls", feature = "async-std"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "rustls", feature = "async-std"))))] -impl OnionClient { +impl OnionTransport { pub fn builder() -> OnionBuilder { let runtime = tor_rtcompat::async_std::AsyncStdRustlsRuntime::current() .expect("Couldn't get the current async_std rustls runtime"); @@ -97,7 +105,7 @@ impl OnionClient { #[cfg(all(feature = "native-tls", feature = "tokio"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "native-tls", feature = "tokio"))))] -impl OnionClient { +impl OnionTransport { pub fn builder() -> OnionBuilder { let runtime = tor_rtcompat::tokio::TokioNativeTlsRuntime::current() .expect("Couldn't get the current tokio native-tls runtime"); @@ -108,7 +116,7 @@ impl OnionClient { #[cfg(all(feature = "rustls", feature = "tokio"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "rustls", feature = "tokio"))))] -impl OnionClient { +impl OnionTransport { pub fn builder() -> OnionBuilder { let runtime = tor_rtcompat::tokio::TokioRustlsRuntime::current() .expect("Couldn't get the current tokio rustls runtime"); @@ -119,23 +127,26 @@ impl OnionClient { #[cfg(all(feature = "native-tls", feature = "async-std"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "native-tls", feature = "async-std"))))] -pub type OnionAsyncStdNativeTlsClient = - OnionClient; +pub type OnionAsyncStdNativeTlsTransport = + OnionTransport; #[cfg(all(feature = "rustls", feature = "async-std"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "rustls", feature = "async-std"))))] -pub type OnionAsyncStdRustlsClient = OnionClient; +pub type OnionAsyncStdRustlsTransport = + OnionTransport; #[cfg(all(feature = "native-tls", feature = "tokio"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "native-tls", feature = "tokio"))))] -pub type OnionTokioNativeTlsClient = OnionClient; +pub type OnionTokioNativeTlsTransport = + OnionTransport; #[cfg(all(feature = "rustls", feature = "tokio"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "rustls", feature = "tokio"))))] -pub type OnionTokioRustlsClient = OnionClient; +pub type OnionTokioRustlsTransport = + OnionTransport; #[derive(Debug, Clone, Copy, Default)] -pub struct AlwaysErrorListenerUpgrade; +pub struct AlwaysErrorListenerUpgrade(PhantomData); -impl core::future::Future for AlwaysErrorListenerUpgrade { - type Output = Result; +impl core::future::Future for AlwaysErrorListenerUpgrade { + type Output = Result; fn poll( self: std::pin::Pin<&mut Self>, _cx: &mut std::task::Context<'_>, @@ -144,11 +155,14 @@ impl core::future::Future for AlwaysErrorListenerUpgrade { } } -impl Transport for OnionClient { - type Output = OnionStream; +impl Transport for OnionTransport +where + S: OnionStream, +{ + type Output = S; type Error = OnionError; type Dial = BoxFuture<'static, Result>; - type ListenerUpgrade = AlwaysErrorListenerUpgrade; + type ListenerUpgrade = AlwaysErrorListenerUpgrade; /// Always returns `TransportError::MultiaddrNotSupported` fn listen_on( @@ -175,14 +189,7 @@ impl Transport for OnionClient { } .map_err(|_| TransportError::MultiaddrNotSupported(addr))?; let onion_client = self.client.clone(); - Ok(async move { - onion_client - .connect(tor_address) - .await - .map(OnionStream::new) - .map_err(OnionError::ArtiErr) - } - .boxed()) + Ok(async move { onion_client.connect(tor_address).await.map(S::from) }.boxed()) } fn dial_as_listener( diff --git a/transports/onion/src/provider.rs b/transports/onion/src/provider.rs index 3685c4d70db..3eb6f28829b 100644 --- a/transports/onion/src/provider.rs +++ b/transports/onion/src/provider.rs @@ -1,23 +1,35 @@ +#[cfg(feature = "tokio")] use std::pin::Pin; use arti_client::DataStream; use futures::{AsyncRead, AsyncWrite}; +pub trait OnionStream: AsyncRead + AsyncWrite + From {} + +impl OnionStream for DataStream {} + +#[cfg(feature = "tokio")] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] #[derive(Debug)] -pub struct OnionStream { +pub struct OnionTokioStream { inner: DataStream, } -impl OnionStream { - #[inline] - pub(super) fn new(inner: DataStream) -> Self { +#[cfg(feature = "tokio")] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +impl From for OnionTokioStream { + fn from(inner: DataStream) -> Self { Self { inner } } } #[cfg(feature = "tokio")] #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] -impl AsyncRead for OnionStream { +impl OnionStream for OnionTokioStream {} + +#[cfg(feature = "tokio")] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] +impl AsyncRead for OnionTokioStream { fn poll_read( mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>, @@ -35,7 +47,7 @@ impl AsyncRead for OnionStream { #[cfg(feature = "tokio")] #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] -impl AsyncWrite for OnionStream { +impl AsyncWrite for OnionTokioStream { #[inline] fn poll_write( mut self: std::pin::Pin<&mut Self>, @@ -70,63 +82,3 @@ impl AsyncWrite for OnionStream { tokio_crate::io::AsyncWrite::poll_write_vectored(Pin::new(&mut self.inner), cx, bufs) } } - -#[cfg(all(feature = "async-std", not(feature = "tokio")))] -#[cfg_attr(docsrs, doc(cfg(feature = "async-std")))] -impl AsyncRead for OnionStream { - #[inline] - fn poll_read( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &mut [u8], - ) -> std::task::Poll> { - Pin::new(&mut self.inner).poll_read(cx, buf) - } - - #[inline] - fn poll_read_vectored( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - bufs: &mut [std::io::IoSliceMut<'_>], - ) -> std::task::Poll> { - Pin::new(&mut self.inner).poll_read_vectored(cx, bufs) - } -} - -#[cfg(all(feature = "async-std", not(feature = "tokio")))] -#[cfg_attr(docsrs, doc(cfg(feature = "async-std")))] -impl AsyncWrite for OnionStream { - #[inline] - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - buf: &[u8], - ) -> std::task::Poll> { - Pin::new(&mut self.inner).poll_write(cx, buf) - } - - #[inline] - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - Pin::new(&mut self.inner).poll_flush(cx) - } - - #[inline] - fn poll_close( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - Pin::new(&mut self.inner).poll_close(cx) - } - - #[inline] - fn poll_write_vectored( - mut self: Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - bufs: &[std::io::IoSlice<'_>], - ) -> std::task::Poll> { - Pin::new(&mut self).poll_write_vectored(cx, bufs) - } -} From e7db75f6547b5351ec58ddf7a23f746d552c9c6a Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 20 Sep 2022 11:48:48 +0200 Subject: [PATCH 14/47] Small module doc --- transports/onion/src/lib.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/transports/onion/src/lib.rs b/transports/onion/src/lib.rs index adae7ad1e91..6e0c17a9b3e 100644 --- a/transports/onion/src/lib.rs +++ b/transports/onion/src/lib.rs @@ -1,6 +1,23 @@ #![doc(html_logo_url = "https://libp2p.io/img/logo_small.png")] #![doc(html_favicon_url = "https://libp2p.io/img/favicon.png")] #![cfg_attr(docsrs, feature(doc_cfg))] +//! Tor based transport for libp2p. Connect through the Tor network to TCP listeners. +//! +//! Main entrypoint of the crate: [OnionTransport] +//! +//! ## Example +//! ```no_run +//! # use async_std_crate as async_std; +//! # use libp2p_core::Transport; +//! # async fn test_func() -> Result<(), Box> { +//! let address = "/dns/www.torproject.org/tcp/1000".parse()?; +//! let mut transport = libp2p_onion::OnionAsyncStdNativeTlsTransport::bootstrapped().await?; +//! // we have achieved tor connection +//! let _conn = transport.dial(address)?.await?; +//! # Ok(()) +//! # } +//! # async_std::task::block_on(async { test_func().await.unwrap() }); +//! ``` use std::sync::Arc; use std::{marker::PhantomData, pin::Pin}; @@ -35,12 +52,13 @@ pub struct OnionTransport { phantom: PhantomData, } +/// Configure the onion transport from here. pub type OnionBuilder = TorClientBuilder; /// Mode of address conversion. Refer tor [arti_client::TorAddr](https://docs.rs/arti-client/latest/arti_client/struct.TorAddr.html) for details. #[derive(Debug, Clone, Copy, Hash, Default, PartialEq, Eq, PartialOrd, Ord)] pub enum AddressConversion { - /// uses only dns for address resolution + /// uses only dns for address resolution (default) #[default] DnsOnly, /// uses ip and dns for addresses From c6335b55f6530c612393676aef4c980da42e9cc2 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 20 Sep 2022 12:03:51 +0200 Subject: [PATCH 15/47] compiles again on wasm32 --- Cargo.toml | 8 ++++---- src/lib.rs | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8f2a966262d..a8d7420a752 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,10 +70,10 @@ yamux = ["dep:libp2p-yamux"] secp256k1 = ["libp2p-core/secp256k1"] rsa = ["libp2p-core/rsa"] serde = ["libp2p-core/serde", "libp2p-kad?/serde", "libp2p-gossipsub?/serde"] -onion-async-std = ["libp2p-onion/async-std"] -onion-tokio = ["libp2p-onion/tokio"] -onion-native-tls = ["libp2p-onion/native-tls"] -onion-rustls = ["libp2p-onion/rustls"] +onion-async-std = ["dep:libp2p-onion", "libp2p-onion?/async-std"] +onion-tokio = ["dep:libp2p-onion", "libp2p-onion?/tokio"] +onion-native-tls = ["dep:libp2p-onion", "libp2p-onion?/native-tls"] +onion-rustls = ["dep:libp2p-onion", "libp2p-onion?/rustls"] [package.metadata.docs.rs] all-features = true diff --git a/src/lib.rs b/src/lib.rs index e9919958d31..a71c69ccf4d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -101,6 +101,7 @@ pub use libp2p_mplex as mplex; pub use libp2p_noise as noise; #[cfg(any(feature = "onion-async-std", feature = "onion-tokio"))] #[cfg_attr(docsrs, doc(cfg(feature = "onion")))] +#[cfg(not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")))] #[doc(inline)] pub use libp2p_onion as onion; #[cfg(feature = "ping")] From 8c20c3010568506d40ed47be1d6db15c306313bb Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Wed, 21 Sep 2022 13:12:33 +1000 Subject: [PATCH 16/47] Refactor multiaddress parsing to avoid clones --- transports/onion/src/address.rs | 158 ++++++++++++++++++++++---------- transports/onion/src/lib.rs | 15 +-- 2 files changed, 119 insertions(+), 54 deletions(-) diff --git a/transports/onion/src/address.rs b/transports/onion/src/address.rs index d863180cf77..5ea406b8b99 100644 --- a/transports/onion/src/address.rs +++ b/transports/onion/src/address.rs @@ -1,79 +1,143 @@ -use std::net::{IpAddr, SocketAddr}; - -use arti_client::{IntoTorAddr, TorAddr, TorAddrError}; +use arti_client::{DangerouslyIntoTorAddr, IntoTorAddr, TorAddr}; use libp2p_core::{multiaddr::Protocol, Multiaddr}; +use std::net::SocketAddr; + +/// "Dangerously" extract a Tor address from the provided [`Multiaddr`]. +/// +/// See [`DangerouslyIntoTorAddr`] for details around the safety / privacy considerations. +pub fn dangerous_extract_tor_address(multiaddr: &Multiaddr) -> Option { + if let Some(tor_addr) = safe_extract_tor_address(multiaddr) { + return Some(tor_addr); + } + + let mut protocols = multiaddr.into_iter(); -pub fn dangerous_extract_tor_address(multiaddr: &Multiaddr) -> Result { - let socket_addr = try_extract_socket_addr(&mut multiaddr.clone())?; - TorAddr::dangerously_from(socket_addr) + let tor_addr = try_to_socket_addr(&protocols.next()?, &protocols.next()?)? + .into_tor_addr_dangerously() + .ok()?; + + Some(tor_addr) } -pub fn safe_extract_tor_address(multiaddr: &mut Multiaddr) -> Result { - let tcp_variant = multiaddr.pop().ok_or(TorAddrError::NoPort)?; - let tcp_port = match tcp_variant { - Protocol::Tcp(p) => p, - _ => return Err(TorAddrError::NoPort), - }; - let dns_variant = multiaddr.pop().ok_or(TorAddrError::InvalidHostname)?; - let dns_addr = match dns_variant { - Protocol::Dns(dns) => dns, - Protocol::Dns4(dns) => dns, - Protocol::Dns6(dns) => dns, - _ => return Err(TorAddrError::InvalidHostname), - }; - let address_tuple = (dns_addr.as_ref(), tcp_port); - address_tuple.into_tor_addr() +/// "Safely" extract a Tor address from the provided [`Multiaddr`]. +/// +/// See [`IntoTorAddr`] for details around the safety / privacy considerations. +pub fn safe_extract_tor_address(multiaddr: &Multiaddr) -> Option { + let mut protocols = multiaddr.into_iter(); + + let tor_addr = try_to_domain_and_port(&protocols.next()?, &protocols.next()?)? + .into_tor_addr() + .ok()?; + + Some(tor_addr) } -fn try_extract_socket_addr(multiaddr: &mut Multiaddr) -> Result { - let tcp_variant = multiaddr.pop().ok_or(TorAddrError::NoPort)?; - let port = match tcp_variant { - Protocol::Tcp(p) => p, - _ => return Err(TorAddrError::NoPort), - }; - let ip_variant = multiaddr.pop().ok_or(TorAddrError::InvalidHostname)?; - let ip = match ip_variant { - Protocol::Ip4(ip4) => IpAddr::V4(ip4), - Protocol::Ip6(ip6) => IpAddr::V6(ip6), - _ => return Err(TorAddrError::InvalidHostname), - }; - Ok(SocketAddr::new(ip, port)) +fn try_to_domain_and_port<'a, 'b>( + maybe_domain: &'a Protocol, + maybe_port: &'b Protocol, +) -> Option<(&'a str, u16)> { + match (maybe_domain, maybe_port) { + ( + Protocol::Dns(domain) | Protocol::Dns4(domain) | Protocol::Dns6(domain), + Protocol::Tcp(port), + ) => Some((domain.as_ref(), *port)), + _ => None, + } +} + +fn try_to_socket_addr(maybe_ip: &Protocol, maybe_port: &Protocol) -> Option { + match (maybe_ip, maybe_port) { + (Protocol::Ip4(ip), Protocol::Tcp(port)) => Some(SocketAddr::from((*ip, *port))), + (Protocol::Ip6(ip), Protocol::Tcp(port)) => Some(SocketAddr::from((*ip, *port))), + _ => None, + } } #[cfg(test)] mod tests { - use std::ops::Not; - - use super::safe_extract_tor_address; + use super::*; + use arti_client::TorAddr; + use std::net::{Ipv4Addr, Ipv6Addr}; #[test] - fn extract_correct_address() { - let mut addresses = [ + fn extract_correct_address_from_dns() { + let addresses = [ "/dns/ip.tld/tcp/10".parse().unwrap(), "/dns4/dns.ip4.tld/tcp/11".parse().unwrap(), "/dns6/dns.ip6.tld/tcp/12".parse().unwrap(), ]; - let all_correct = addresses - .iter_mut() - .map(safe_extract_tor_address) - .all(|extract_res| extract_res.is_ok() && extract_res.unwrap().is_ip_address().not()); + let actual = addresses + .iter() + .filter_map(safe_extract_tor_address) + .collect::>(); + + assert_eq!( + &[ + TorAddr::from(("ip.tld", 10)).unwrap(), + TorAddr::from(("dns.ip4.tld", 11)).unwrap(), + TorAddr::from(("dns.ip6.tld", 12)).unwrap(), + ], + actual.as_slice() + ); + } + + #[test] + fn extract_correct_address_from_ips() { + let addresses = [ + "/ip4/127.0.0.1/tcp/10".parse().unwrap(), + "/ip6/::1/tcp/10".parse().unwrap(), + ]; + + let actual = addresses + .iter() + .filter_map(dangerous_extract_tor_address) + .collect::>(); - assert!(all_correct, "Not all addresses have been parsed correctly"); + assert_eq!( + &[ + TorAddr::dangerously_from((Ipv4Addr::LOCALHOST, 10)).unwrap(), + TorAddr::dangerously_from((Ipv6Addr::LOCALHOST, 10)).unwrap(), + ], + actual.as_slice() + ); + } + + #[test] + fn dangerous_extract_works_on_domains_too() { + let addresses = [ + "/dns/ip.tld/tcp/10".parse().unwrap(), + "/ip4/127.0.0.1/tcp/10".parse().unwrap(), + "/ip6/::1/tcp/10".parse().unwrap(), + ]; + + let actual = addresses + .iter() + .filter_map(dangerous_extract_tor_address) + .collect::>(); + + assert_eq!( + &[ + TorAddr::from(("ip.tld", 10)).unwrap(), + TorAddr::dangerously_from((Ipv4Addr::LOCALHOST, 10)).unwrap(), + TorAddr::dangerously_from((Ipv6Addr::LOCALHOST, 10)).unwrap(), + ], + actual.as_slice() + ); } #[test] fn detect_incorrect_address() { - let mut addresses = [ + let addresses = [ "/tcp/10/udp/12".parse().unwrap(), "/dns/ip.tld/dns4/ip.tld/dns6/ip.tld".parse().unwrap(), "/tcp/10/ip4/1.1.1.1".parse().unwrap(), ]; let all_correct = addresses - .iter_mut() + .iter() .map(safe_extract_tor_address) - .all(|res| res == Err(arti_client::TorAddrError::NoPort)); + .all(|res| res.is_none()); assert!( all_correct, diff --git a/transports/onion/src/lib.rs b/transports/onion/src/lib.rs index 6e0c17a9b3e..5e57b9a064a 100644 --- a/transports/onion/src/lib.rs +++ b/transports/onion/src/lib.rs @@ -199,14 +199,15 @@ where false } - fn dial(&mut self, mut addr: Multiaddr) -> Result> { - let tor_address = if self.conversion_mode == AddressConversion::IpAndDns { - safe_extract_tor_address(&mut addr).or_else(|_| dangerous_extract_tor_address(&addr)) - } else { - safe_extract_tor_address(&mut addr) - } - .map_err(|_| TransportError::MultiaddrNotSupported(addr))?; + fn dial(&mut self, addr: Multiaddr) -> Result> { + let maybe_tor_addr = match self.conversion_mode { + AddressConversion::DnsOnly => safe_extract_tor_address(&addr), + AddressConversion::IpAndDns => dangerous_extract_tor_address(&addr), + }; + + let tor_address = maybe_tor_addr.ok_or(TransportError::MultiaddrNotSupported(addr))?; let onion_client = self.client.clone(); + Ok(async move { onion_client.connect(tor_address).await.map(S::from) }.boxed()) } From b6b2aaccbfe6068506f125a4ae43c9335e504c9e Mon Sep 17 00:00:00 2001 From: Hannes <55623006+umgefahren@users.noreply.github.com> Date: Wed, 21 Sep 2022 10:38:07 +0200 Subject: [PATCH 17/47] Apply suggestions from code review Co-authored-by: Thomas Eizinger --- transports/onion/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transports/onion/src/lib.rs b/transports/onion/src/lib.rs index 5e57b9a064a..982f0a730c2 100644 --- a/transports/onion/src/lib.rs +++ b/transports/onion/src/lib.rs @@ -145,7 +145,7 @@ impl OnionTransport { #[cfg(all(feature = "native-tls", feature = "async-std"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "native-tls", feature = "async-std"))))] -pub type OnionAsyncStdNativeTlsTransport = +pub type AsyncStdNativeTlsOnionTransport = OnionTransport; #[cfg(all(feature = "rustls", feature = "async-std"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "rustls", feature = "async-std"))))] From 9d2d94c011b601cbd787df041c25e3b96d64cf1b Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 21 Sep 2022 10:40:47 +0200 Subject: [PATCH 18/47] renamed stuff --- transports/onion/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/transports/onion/src/lib.rs b/transports/onion/src/lib.rs index 982f0a730c2..406c341719f 100644 --- a/transports/onion/src/lib.rs +++ b/transports/onion/src/lib.rs @@ -149,15 +149,15 @@ pub type AsyncStdNativeTlsOnionTransport = OnionTransport; #[cfg(all(feature = "rustls", feature = "async-std"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "rustls", feature = "async-std"))))] -pub type OnionAsyncStdRustlsTransport = +pub type AsyncStdRustlsOnionTransport = OnionTransport; #[cfg(all(feature = "native-tls", feature = "tokio"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "native-tls", feature = "tokio"))))] -pub type OnionTokioNativeTlsTransport = +pub type TokioNativeTlsOnionTransport = OnionTransport; #[cfg(all(feature = "rustls", feature = "tokio"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "rustls", feature = "tokio"))))] -pub type OnionTokioRustlsTransport = +pub type TokioRustlsOnionTransport = OnionTransport; #[derive(Debug, Clone, Copy, Default)] From 4076338b7021909a4e4d699cc580e7d80911865e Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 21 Sep 2022 10:44:19 +0200 Subject: [PATCH 19/47] corrected issues of previous commit an implemented all recently requested changes --- examples/ping-onion.rs | 5 +++-- transports/onion/src/lib.rs | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/ping-onion.rs b/examples/ping-onion.rs index 7675232aefc..82cbf2875e1 100644 --- a/examples/ping-onion.rs +++ b/examples/ping-onion.rs @@ -37,13 +37,14 @@ //! //! On the second computer run: //! ```sh -//! cargo run --example ping-onion -- /ip4/127.0.0.1/tcp/24915 +//! cargo run --example ping-onion -- /ip4/123.45.67.89/tcp/24915 //! ``` //! //! The two nodes establish a connection, negotiate the ping protocol //! and begin pinging each other over Tor. use futures::prelude::*; +use libp2p::onion::AddressConversion; use libp2p::swarm::{Swarm, SwarmEvent}; use libp2p::{ core::upgrade, identity, mplex, noise, onion, ping, yamux, Multiaddr, PeerId, Transport, @@ -58,7 +59,7 @@ async fn onion_transport( > { use std::time::Duration; - let transport = onion::OnionAsyncStdNativeTlsTransport::bootstrapped().await?; + let transport = onion::AsyncStdNativeTlsOnionTransport::bootstrapped().await?.with_address_conversion(AddressConversion::IpAndDns); Ok(transport .upgrade(upgrade::Version::V1) .authenticate( diff --git a/transports/onion/src/lib.rs b/transports/onion/src/lib.rs index 406c341719f..45f3f0b3360 100644 --- a/transports/onion/src/lib.rs +++ b/transports/onion/src/lib.rs @@ -11,7 +11,7 @@ //! # use libp2p_core::Transport; //! # async fn test_func() -> Result<(), Box> { //! let address = "/dns/www.torproject.org/tcp/1000".parse()?; -//! let mut transport = libp2p_onion::OnionAsyncStdNativeTlsTransport::bootstrapped().await?; +//! let mut transport = libp2p_onion::AsyncStdNativeTlsOnionTransport::bootstrapped().await?; //! // we have achieved tor connection //! let _conn = transport.dial(address)?.await?; //! # Ok(()) @@ -82,7 +82,7 @@ impl OnionTransport { self.client.bootstrap().await } - pub fn with_address_conversion(&mut self, conversion_mode: AddressConversion) -> &mut Self { + pub fn with_address_conversion(mut self, conversion_mode: AddressConversion) -> Self { self.conversion_mode = conversion_mode; self } From bf9df0a762a632db9f60d3e66eee9dcedbd39336 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 21 Sep 2022 11:01:34 +0200 Subject: [PATCH 20/47] formatted --- examples/ping-onion.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/ping-onion.rs b/examples/ping-onion.rs index 82cbf2875e1..12c9fdfc763 100644 --- a/examples/ping-onion.rs +++ b/examples/ping-onion.rs @@ -59,7 +59,9 @@ async fn onion_transport( > { use std::time::Duration; - let transport = onion::AsyncStdNativeTlsOnionTransport::bootstrapped().await?.with_address_conversion(AddressConversion::IpAndDns); + let transport = onion::AsyncStdNativeTlsOnionTransport::bootstrapped() + .await? + .with_address_conversion(AddressConversion::IpAndDns); Ok(transport .upgrade(upgrade::Version::V1) .authenticate( From 548a815b0ecf4ba5b48cf7977ef5413d8ef6665a Mon Sep 17 00:00:00 2001 From: Hannes <55623006+umgefahren@users.noreply.github.com> Date: Wed, 28 Sep 2022 10:48:34 +0200 Subject: [PATCH 21/47] Apply suggestions from code review Co-authored-by: Thomas Eizinger Co-authored-by: Max Inden --- transports/onion/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/transports/onion/src/lib.rs b/transports/onion/src/lib.rs index 45f3f0b3360..4f33bb33246 100644 --- a/transports/onion/src/lib.rs +++ b/transports/onion/src/lib.rs @@ -3,7 +3,7 @@ #![cfg_attr(docsrs, feature(doc_cfg))] //! Tor based transport for libp2p. Connect through the Tor network to TCP listeners. //! -//! Main entrypoint of the crate: [OnionTransport] +//! Main entrypoint of the crate: [`OnionTransport`] //! //! ## Example //! ```no_run @@ -42,7 +42,7 @@ pub type OnionError = arti_client::Error; #[derive(Clone)] pub struct OnionTransport { - // client is in an Arc, because wihtout it the Transport::Dial method can't be implemented, + // client is in an Arc, because without it the [`Transport::dial`] method can't be implemented, // due to lifetime issues. With the, eventual, stabilization of static async traits this issue // will be resolved. client: Arc>, @@ -58,7 +58,7 @@ pub type OnionBuilder = TorClientBuilder; /// Mode of address conversion. Refer tor [arti_client::TorAddr](https://docs.rs/arti-client/latest/arti_client/struct.TorAddr.html) for details. #[derive(Debug, Clone, Copy, Hash, Default, PartialEq, Eq, PartialOrd, Ord)] pub enum AddressConversion { - /// uses only dns for address resolution (default) + /// Uses only dns for address resolution (default). #[default] DnsOnly, /// uses ip and dns for addresses From 307f19c46b900d6f2ecac02715126e7ff990e9a2 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 28 Sep 2022 11:03:54 +0200 Subject: [PATCH 22/47] Added license headers and added to README.md --- examples/README.md | 6 ++++++ transports/onion/Cargo.toml | 2 +- transports/onion/src/address.rs | 20 ++++++++++++++++++++ transports/onion/src/lib.rs | 20 ++++++++++++++++++++ transports/onion/src/provider.rs | 20 ++++++++++++++++++++ 5 files changed, 67 insertions(+), 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index 6c16d77cf66..2f094ab452a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -44,6 +44,12 @@ A set of examples showcasing how to use rust-libp2p. Discover peers on the same network via the MDNS protocol. +## Special transports + +- [Ping over Onion](ping-onion.rs) + + Client counterpart to normal ping, that routes traffic over Tor. + ## Integration into a larger application - [File sharing application](./file-sharing.rs) diff --git a/transports/onion/Cargo.toml b/transports/onion/Cargo.toml index 1d51c7f510b..617f5c8547c 100644 --- a/transports/onion/Cargo.toml +++ b/transports/onion/Cargo.toml @@ -24,7 +24,7 @@ async-std-crate = { package = "async-std", version = "1", features = ["attribute [features] default = ["async-std", "native-tls"] tokio = ["arti-client/tokio", "dep:tokio-crate"] -async-std = ["arti-client/async-std"] +async-std = ["arti-client/async-std", "dep:async-std-crate"] native-tls = ["arti-client/native-tls"] rustls = ["arti-client/rustls"] diff --git a/transports/onion/src/address.rs b/transports/onion/src/address.rs index 5ea406b8b99..564f97193f5 100644 --- a/transports/onion/src/address.rs +++ b/transports/onion/src/address.rs @@ -1,3 +1,23 @@ +// Copyright 2022 Hannes Furmans +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + use arti_client::{DangerouslyIntoTorAddr, IntoTorAddr, TorAddr}; use libp2p_core::{multiaddr::Protocol, Multiaddr}; use std::net::SocketAddr; diff --git a/transports/onion/src/lib.rs b/transports/onion/src/lib.rs index 4f33bb33246..444e4155e4d 100644 --- a/transports/onion/src/lib.rs +++ b/transports/onion/src/lib.rs @@ -1,3 +1,23 @@ +// Copyright 2022 Hannes Furmans +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + #![doc(html_logo_url = "https://libp2p.io/img/logo_small.png")] #![doc(html_favicon_url = "https://libp2p.io/img/favicon.png")] #![cfg_attr(docsrs, feature(doc_cfg))] diff --git a/transports/onion/src/provider.rs b/transports/onion/src/provider.rs index 3eb6f28829b..d799f0bf8f8 100644 --- a/transports/onion/src/provider.rs +++ b/transports/onion/src/provider.rs @@ -1,3 +1,23 @@ +// Copyright 2022 Hannes Furmans +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + #[cfg(feature = "tokio")] use std::pin::Pin; From 03ba6e763e922b68b5f2f804e3d014b277143f1d Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 3 Oct 2022 13:35:24 +0200 Subject: [PATCH 23/47] addressed todos by @thomaseizinger --- CHANGELOG.md | 4 ++++ deny.toml | 9 +++++++++ transports/onion/CHANGELOG.md | 6 ++++++ transports/onion/Cargo.toml | 3 +-- 4 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 transports/onion/CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md index f1cf6eed30c..cc9df233d2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ - [`libp2p-uds` CHANGELOG](transports/uds/CHANGELOG.md) - [`libp2p-wasm-ext` CHANGELOG](transports/wasm-ext/CHANGELOG.md) - [`libp2p-websocket` CHANGELOG](transports/websocket/CHANGELOG.md) +- [`libp2p-onion` CHANGELOG](transports/onion/CHANGELOG.md) ## Multiplexers @@ -48,6 +49,9 @@ - Remove default features. You need to enable required features explicitly now. As a quick workaround, you may want to use the new `full` feature which activates all features. See [PR 2918]. +- New `libp2p-onion` crate + ([PR 2899](https://github.com/libp2p/rust-libp2p/pull/2899)). + - Update individual crates. - Update to [`libp2p-autonat` `v0.8.0`](protocols/autonat/CHANGELOG.md#0080). - Update to [`libp2p-core` `v0.37.0`](core/CHANGELOG.md#0370). diff --git a/deny.toml b/deny.toml index 6634887128e..e80b204569d 100644 --- a/deny.toml +++ b/deny.toml @@ -78,6 +78,10 @@ exceptions = [ { allow = ["ISC", "MIT", "OpenSSL"], name = "ring" }, # libp2p is not re-distributing unicode tables data by itself { allow = ["MIT", "Apache-2.0", "Unicode-DFS-2016"], name = "unicode-ident" }, + # libp2p is not re-distributing unicode tables data by iteself + { allow = ["Unicode-DFS-2016"], name = "tinystr"}, + # exception for strange license in arti dependency + { allow = ["GPL-3.0", "BSD-3-Clause"], name = "bounded-vec-deque" }, ] # Some crates don't have (easily) machine readable licensing information, @@ -88,6 +92,11 @@ name = "ring" expression = "ISC AND MIT AND OpenSSL" license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] +[[licenses.clarify]] +name = "bounded-vec-deque" +expression = "GPL-3.0 OR BSD-3-Clause" +license-files = [{ path = "LICENSE", hash = 0x40145f22 }] + [licenses.private] # If true, ignores workspace crates that aren't published, or are only # published to private registries diff --git a/transports/onion/CHANGELOG.md b/transports/onion/CHANGELOG.md new file mode 100644 index 00000000000..bfd942e0b45 --- /dev/null +++ b/transports/onion/CHANGELOG.md @@ -0,0 +1,6 @@ +# 0.37.0 - [unreleased] + +- Initial Implementation + +[PR 2899]: https://github.com/libp2p/rust-libp2p/pull/2899 + diff --git a/transports/onion/Cargo.toml b/transports/onion/Cargo.toml index 617f5c8547c..944b037a654 100644 --- a/transports/onion/Cargo.toml +++ b/transports/onion/Cargo.toml @@ -7,7 +7,7 @@ resolver = "2" [dependencies] -libp2p-core = { version = "0.36.0", path = "../../core", default-features = false } +libp2p-core = { version = "0.37", path = "../../core" } futures = "0.3" arti-client = { version = "0.6", default-features = false } thiserror = "1" @@ -22,7 +22,6 @@ async-std-crate = { package = "async-std", version = "1", features = ["attribute [features] -default = ["async-std", "native-tls"] tokio = ["arti-client/tokio", "dep:tokio-crate"] async-std = ["arti-client/async-std", "dep:async-std-crate"] native-tls = ["arti-client/native-tls"] From e6a51c04ea67fd705d4586559c8b73f2d2953b2b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 4 Oct 2022 11:40:57 +1100 Subject: [PATCH 24/47] Remove `cargo-deny` exception The licenses are fine, we don't need an exception. We just have to _clarify_ to `cargo deny`, what license it is. --- deny.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/deny.toml b/deny.toml index e80b204569d..989a2892f9c 100644 --- a/deny.toml +++ b/deny.toml @@ -80,8 +80,6 @@ exceptions = [ { allow = ["MIT", "Apache-2.0", "Unicode-DFS-2016"], name = "unicode-ident" }, # libp2p is not re-distributing unicode tables data by iteself { allow = ["Unicode-DFS-2016"], name = "tinystr"}, - # exception for strange license in arti dependency - { allow = ["GPL-3.0", "BSD-3-Clause"], name = "bounded-vec-deque" }, ] # Some crates don't have (easily) machine readable licensing information, From 8de49cabad37c2ce0d5efdca2f174b714ac9ceae Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 4 Oct 2022 11:41:33 +1100 Subject: [PATCH 25/47] Remove unnecessary empty lines --- transports/onion/Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/transports/onion/Cargo.toml b/transports/onion/Cargo.toml index 944b037a654..a34d95a9051 100644 --- a/transports/onion/Cargo.toml +++ b/transports/onion/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" license = "MIT" resolver = "2" - [dependencies] libp2p-core = { version = "0.37", path = "../../core" } futures = "0.3" @@ -20,7 +19,6 @@ libp2p = { version = "0.49", path = "../../" } tokio-crate = { package = "tokio", version = "1", features = ["rt", "macros"] } async-std-crate = { package = "async-std", version = "1", features = ["attributes"] } - [features] tokio = ["arti-client/tokio", "dep:tokio-crate"] async-std = ["arti-client/async-std", "dep:async-std-crate"] From 1492557a14cd9d0ee87289c52171f75f7189fafe Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 4 Oct 2022 11:41:41 +1100 Subject: [PATCH 26/47] Merge `use` statements into one block --- transports/onion/src/lib.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/transports/onion/src/lib.rs b/transports/onion/src/lib.rs index 444e4155e4d..cb1ebcca73a 100644 --- a/transports/onion/src/lib.rs +++ b/transports/onion/src/lib.rs @@ -39,13 +39,13 @@ //! # async_std::task::block_on(async { test_func().await.unwrap() }); //! ``` -use std::sync::Arc; -use std::{marker::PhantomData, pin::Pin}; - use address::{dangerous_extract_tor_address, safe_extract_tor_address}; use arti_client::{DataStream, TorClient, TorClientBuilder}; use futures::{future::BoxFuture, FutureExt}; use libp2p_core::{transport::TransportError, Multiaddr, Transport}; +use provider::OnionStream; +use std::sync::Arc; +use std::{marker::PhantomData, pin::Pin}; use tor_rtcompat::Runtime; mod address; @@ -56,8 +56,6 @@ mod provider; #[doc(inline)] pub use provider::OnionTokioStream; -use provider::OnionStream; - pub type OnionError = arti_client::Error; #[derive(Clone)] From 402835d766f282b76b00a426a031404a0321d71b Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 4 Oct 2022 11:42:14 +1100 Subject: [PATCH 27/47] Make use of link in changelog --- transports/onion/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transports/onion/CHANGELOG.md b/transports/onion/CHANGELOG.md index bfd942e0b45..f9546572918 100644 --- a/transports/onion/CHANGELOG.md +++ b/transports/onion/CHANGELOG.md @@ -1,6 +1,6 @@ # 0.37.0 - [unreleased] -- Initial Implementation +- Initial Implementation. See [PR 2899]. [PR 2899]: https://github.com/libp2p/rust-libp2p/pull/2899 From b292cd059b477a7e0ba9932854d3837be6adad92 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 4 Oct 2022 11:49:14 +1100 Subject: [PATCH 28/47] Consistently reference `Pin` with FQP to avoid import --- transports/onion/src/provider.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/transports/onion/src/provider.rs b/transports/onion/src/provider.rs index d799f0bf8f8..124e583afeb 100644 --- a/transports/onion/src/provider.rs +++ b/transports/onion/src/provider.rs @@ -18,9 +18,6 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -#[cfg(feature = "tokio")] -use std::pin::Pin; - use arti_client::DataStream; use futures::{AsyncRead, AsyncWrite}; @@ -51,13 +48,13 @@ impl OnionStream for OnionTokioStream {} #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] impl AsyncRead for OnionTokioStream { fn poll_read( - mut self: Pin<&mut Self>, + mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, buf: &mut [u8], ) -> std::task::Poll> { let mut read_buf = tokio_crate::io::ReadBuf::new(buf); futures::ready!(tokio_crate::io::AsyncRead::poll_read( - Pin::new(&mut self.inner), + std::pin::Pin::new(&mut self.inner), cx, &mut read_buf ))?; @@ -95,7 +92,7 @@ impl AsyncWrite for OnionTokioStream { #[inline] fn poll_write_vectored( - mut self: Pin<&mut Self>, + mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, bufs: &[std::io::IoSlice<'_>], ) -> std::task::Poll> { From 85f6eb65ac493111b0d65a88e08e62ccc8f90a27 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 4 Oct 2022 11:55:03 +1100 Subject: [PATCH 29/47] Update `arti` to latest version --- transports/onion/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/transports/onion/Cargo.toml b/transports/onion/Cargo.toml index a34d95a9051..120ed32792b 100644 --- a/transports/onion/Cargo.toml +++ b/transports/onion/Cargo.toml @@ -8,9 +8,9 @@ resolver = "2" [dependencies] libp2p-core = { version = "0.37", path = "../../core" } futures = "0.3" -arti-client = { version = "0.6", default-features = false } +arti-client = { version = "0.7", default-features = false } thiserror = "1" -tor-rtcompat = "0.6" +tor-rtcompat = "0.7" tokio-crate = { package = "tokio", version = "1", optional = true, default-features = false } async-std-crate = { package = "async-std", version = "1", optional = true, default-features = false } From 38ba200071b387cb9c50d4025f9c181128ee6175 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 4 Oct 2022 11:55:17 +1100 Subject: [PATCH 30/47] Sort dependencies alphabetically --- transports/onion/Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/transports/onion/Cargo.toml b/transports/onion/Cargo.toml index 120ed32792b..48a2f67e30b 100644 --- a/transports/onion/Cargo.toml +++ b/transports/onion/Cargo.toml @@ -6,13 +6,13 @@ license = "MIT" resolver = "2" [dependencies] -libp2p-core = { version = "0.37", path = "../../core" } -futures = "0.3" arti-client = { version = "0.7", default-features = false } +async-std-crate = { package = "async-std", version = "1", optional = true, default-features = false } +futures = "0.3" +libp2p-core = { version = "0.37", path = "../../core" } thiserror = "1" -tor-rtcompat = "0.7" tokio-crate = { package = "tokio", version = "1", optional = true, default-features = false } -async-std-crate = { package = "async-std", version = "1", optional = true, default-features = false } +tor-rtcompat = "0.7" [dev-dependencies] libp2p = { version = "0.49", path = "../../" } From 077ab943f69d0e24d83dbb75631fbf74bf199028 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 4 Oct 2022 11:56:32 +1100 Subject: [PATCH 31/47] Use link reference in CHANGELOG.md --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc9df233d2a..ccd9ee26f44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,8 +49,7 @@ - Remove default features. You need to enable required features explicitly now. As a quick workaround, you may want to use the new `full` feature which activates all features. See [PR 2918]. -- New `libp2p-onion` crate - ([PR 2899](https://github.com/libp2p/rust-libp2p/pull/2899)). +- New `libp2p-onion` crate. See [PR 2899]. - Update individual crates. - Update to [`libp2p-autonat` `v0.8.0`](protocols/autonat/CHANGELOG.md#0080). @@ -79,7 +78,8 @@ - Update to [`libp2p-websocket` `v0.39.0`](transports/websocket/CHANGELOG.md#0390). - Update to [`libp2p-yamux` `v0.41.0`](muxers/mplex/CHANGELOG.md#0410). -- [PR 2918]: https://github.com/libp2p/rust-libp2p/pull/2918 +[PR 2918]: https://github.com/libp2p/rust-libp2p/pull/2918 +[PR 2899]: https://github.com/libp2p/rust-libp2p/pull/2899 # 0.48.0 From d57fe4140c73634e7511bb734c3250d436b76c63 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 4 Oct 2022 11:59:51 +1100 Subject: [PATCH 32/47] Fix compile errors --- transports/onion/src/provider.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/transports/onion/src/provider.rs b/transports/onion/src/provider.rs index 124e583afeb..c0b75afa235 100644 --- a/transports/onion/src/provider.rs +++ b/transports/onion/src/provider.rs @@ -71,7 +71,7 @@ impl AsyncWrite for OnionTokioStream { cx: &mut std::task::Context<'_>, buf: &[u8], ) -> std::task::Poll> { - tokio_crate::io::AsyncWrite::poll_write(Pin::new(&mut self.inner), cx, buf) + tokio_crate::io::AsyncWrite::poll_write(std::pin::Pin::new(&mut self.inner), cx, buf) } #[inline] @@ -79,7 +79,7 @@ impl AsyncWrite for OnionTokioStream { mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - tokio_crate::io::AsyncWrite::poll_flush(Pin::new(&mut self.inner), cx) + tokio_crate::io::AsyncWrite::poll_flush(std::pin::Pin::new(&mut self.inner), cx) } #[inline] @@ -87,7 +87,7 @@ impl AsyncWrite for OnionTokioStream { mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { - tokio_crate::io::AsyncWrite::poll_shutdown(Pin::new(&mut self.inner), cx) + tokio_crate::io::AsyncWrite::poll_shutdown(std::pin::Pin::new(&mut self.inner), cx) } #[inline] @@ -96,6 +96,10 @@ impl AsyncWrite for OnionTokioStream { cx: &mut std::task::Context<'_>, bufs: &[std::io::IoSlice<'_>], ) -> std::task::Poll> { - tokio_crate::io::AsyncWrite::poll_write_vectored(Pin::new(&mut self.inner), cx, bufs) + tokio_crate::io::AsyncWrite::poll_write_vectored( + std::pin::Pin::new(&mut self.inner), + cx, + bufs, + ) } } From bd7f8ac21a23a1eeccbffda5a6dfb5e530d53d9c Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 4 Oct 2022 17:25:44 +0200 Subject: [PATCH 33/47] Really enable all features in full --- Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0008537b8d3..9c6ae3c5e2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,9 @@ full = [ "websocket", "yamux", "onion-async-std", - "onion-native-tls" + "onion-tokio", + "onion-native-tls", + "onion-rustls", ] autonat = ["dep:libp2p-autonat"] dcutr = ["dep:libp2p-dcutr", "libp2p-metrics?/dcutr"] From f834d9e0ab5709ec6208351593a107a7e525709b Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Mon, 10 Oct 2022 21:37:09 +0200 Subject: [PATCH 34/47] Adjusted example to match the ping example --- examples/ping-onion.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/examples/ping-onion.rs b/examples/ping-onion.rs index 12c9fdfc763..e60a74d99e1 100644 --- a/examples/ping-onion.rs +++ b/examples/ping-onion.rs @@ -45,9 +45,10 @@ use futures::prelude::*; use libp2p::onion::AddressConversion; -use libp2p::swarm::{Swarm, SwarmEvent}; +use libp2p::swarm::{Swarm, SwarmEvent, keep_alive}; use libp2p::{ core::upgrade, identity, mplex, noise, onion, ping, yamux, Multiaddr, PeerId, Transport, + NetworkBehaviour }; use std::error::Error; @@ -83,17 +84,9 @@ async fn main() -> Result<(), Box> { let local_peer_id = PeerId::from(local_key.public()); println!("Local peer id: {:?}", local_peer_id); - // create a transport let transport = onion_transport(local_key).await?; - // Create a ping network behaviour. - // - // For illustrative purposes, the ping protocol is configured to - // keep the connection alive, so a continuous sequence of pings - // can be observed. - let behaviour = ping::Behaviour::new(ping::Config::new().with_keep_alive(true)); - - let mut swarm = Swarm::new(transport, behaviour, local_peer_id); + let mut swarm = Swarm::new(transport, Behaviour::default(), local_peer_id); // Dial the peer identified by the multi-address given as the second // command-line argument, if any. @@ -115,3 +108,13 @@ async fn main() -> Result<(), Box> { } } } + +/// Our network behaviour. +/// +/// For illustrative purposes, this includes the [`KeepAlive`](behaviour::KeepAlive) behaviour so a continuous sequence of +/// pings can be observed. +#[derive(NetworkBehaviour, Default)] +struct Behaviour { + keep_alive: keep_alive::Behaviour, + ping: ping::Behaviour, +} From 1a5668bdc41ecd7773a9191f8a105ee5bc9964fe Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 11 Oct 2022 21:26:29 +1100 Subject: [PATCH 35/47] Fix formatting --- examples/ping-onion.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/ping-onion.rs b/examples/ping-onion.rs index e60a74d99e1..c15d68ce478 100644 --- a/examples/ping-onion.rs +++ b/examples/ping-onion.rs @@ -45,10 +45,10 @@ use futures::prelude::*; use libp2p::onion::AddressConversion; -use libp2p::swarm::{Swarm, SwarmEvent, keep_alive}; +use libp2p::swarm::{keep_alive, Swarm, SwarmEvent}; use libp2p::{ - core::upgrade, identity, mplex, noise, onion, ping, yamux, Multiaddr, PeerId, Transport, - NetworkBehaviour + core::upgrade, identity, mplex, noise, onion, ping, yamux, Multiaddr, NetworkBehaviour, PeerId, + Transport, }; use std::error::Error; From 9bcd2be6ed17cb633981c3f92a66e0ff4ac936d2 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 18 Oct 2022 08:46:00 +0200 Subject: [PATCH 36/47] Changed CHANGELOG.md entry --- CHANGELOG.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b702237108..224d7738c91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,13 +44,18 @@ # `libp2p` facade crate +# 0.50.0 [unreleased] + +- New `libp2p-onion` crate. See [PR 2899]. + +[PR 2899]: https://github.com/libp2p/rust-libp2p/pull/2899 + # 0.49.0 - Remove default features. You need to enable required features explicitly now. As a quick workaround, you may want to use the new `full` feature which activates all features. See [PR 2918]. -- New `libp2p-onion` crate. See [PR 2899]. - Introduce `tokio` and `async-std` features and deprecate the following ones: - `tcp-tokio` in favor of `tcp` + `tokio` - `mdns-tokio` in favor of `mdns` + `tokio` @@ -89,7 +94,6 @@ - Update to [`libp2p-yamux` `v0.41.0`](muxers/mplex/CHANGELOG.md#0410). [PR 2918]: https://github.com/libp2p/rust-libp2p/pull/2918 -[PR 2899]: https://github.com/libp2p/rust-libp2p/pull/2899 [PR 2962]: https://github.com/libp2p/rust-libp2p/pull/2962 # 0.48.0 From 65bc2675d530f9f3b46f78f299689d9995bb1db6 Mon Sep 17 00:00:00 2001 From: Hannes <55623006+umgefahren@users.noreply.github.com> Date: Wed, 19 Oct 2022 11:54:41 +0200 Subject: [PATCH 37/47] Update CHANGELOG.md Co-authored-by: Thomas Eizinger --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 224d7738c91..fb313ef46e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,7 +55,6 @@ - Remove default features. You need to enable required features explicitly now. As a quick workaround, you may want to use the new `full` feature which activates all features. See [PR 2918]. - - Introduce `tokio` and `async-std` features and deprecate the following ones: - `tcp-tokio` in favor of `tcp` + `tokio` - `mdns-tokio` in favor of `mdns` + `tokio` From 40765c612054eb6bebcebb4f88eea165094aaae7 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 19 Oct 2022 13:18:17 +0200 Subject: [PATCH 38/47] Updated Cargo.toml responding to the requested changes --- Cargo.toml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 73f5cc1fcec..01de9b68edf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,9 +50,9 @@ full = [ "websocket", "yamux", "onion-async-std", - "onion-tokio", "onion-native-tls", "onion-rustls", + "onion-tokio", ] autonat = ["dep:libp2p-autonat"] dcutr = ["dep:libp2p-dcutr", "libp2p-metrics?/dcutr"] @@ -89,8 +89,7 @@ secp256k1 = ["libp2p-core/secp256k1"] rsa = ["libp2p-core/rsa"] ecdsa = ["libp2p-core/ecdsa"] serde = ["libp2p-core/serde", "libp2p-kad?/serde", "libp2p-gossipsub?/serde"] -onion-async-std = ["dep:libp2p-onion", "libp2p-onion?/async-std"] -onion-tokio = ["dep:libp2p-onion", "libp2p-onion?/tokio"] +onion = ["dep:libp2p-onion"] onion-native-tls = ["dep:libp2p-onion", "libp2p-onion?/native-tls"] onion-rustls = ["dep:libp2p-onion", "libp2p-onion?/rustls"] tokio = ["libp2p-mdns?/tokio", "libp2p-tcp?/tokio", "libp2p-dns?/tokio", "libp2p-onion?/tokio"] From 1f692de837a9436cea98e80188b81e07a487b59f Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 19 Oct 2022 13:22:27 +0200 Subject: [PATCH 39/47] Corrected error in the feature flag --- Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 01de9b68edf..e940b062ab6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,10 +49,9 @@ full = [ "wasm-ext-websocket", "websocket", "yamux", - "onion-async-std", + "onion", "onion-native-tls", "onion-rustls", - "onion-tokio", ] autonat = ["dep:libp2p-autonat"] dcutr = ["dep:libp2p-dcutr", "libp2p-metrics?/dcutr"] From 76ed916877949c3b06b54cd2122f655b85aaf8f4 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Wed, 19 Oct 2022 14:07:22 +0200 Subject: [PATCH 40/47] Fixed issues with featues --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e940b062ab6..fb0598ba301 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,7 +92,7 @@ onion = ["dep:libp2p-onion"] onion-native-tls = ["dep:libp2p-onion", "libp2p-onion?/native-tls"] onion-rustls = ["dep:libp2p-onion", "libp2p-onion?/rustls"] tokio = ["libp2p-mdns?/tokio", "libp2p-tcp?/tokio", "libp2p-dns?/tokio", "libp2p-onion?/tokio"] -async-std = ["libp2p-mdns?/async-io", "libp2p-tcp?/async-io", "libp2p-dns?/async-std"] +async-std = ["libp2p-mdns?/async-io", "libp2p-tcp?/async-io", "libp2p-dns?/async-std", "libp2p-onion?/async-std"] [package.metadata.docs.rs] all-features = true diff --git a/src/lib.rs b/src/lib.rs index ce08f3de227..1ae3ad09835 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -133,7 +133,7 @@ pub use libp2p_mplex as mplex; #[cfg_attr(docsrs, doc(cfg(feature = "noise")))] #[doc(inline)] pub use libp2p_noise as noise; -#[cfg(any(feature = "onion-async-std", feature = "onion-tokio"))] +#[cfg(feature = "onion")] #[cfg_attr(docsrs, doc(cfg(feature = "onion")))] #[cfg(not(any(target_os = "emscripten", target_os = "wasi", target_os = "unknown")))] #[doc(inline)] From 9332ee3543e17124e86f2a6f57fabf52f2673a6a Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 20 Oct 2022 10:43:05 +1100 Subject: [PATCH 41/47] Alphabetically sort feature list --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fb0598ba301..fd97642b2a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,9 @@ full = [ "metrics", "mplex", "noise", + "onion", + "onion-native-tls", + "onion-rustls", "ping", "plaintext", "pnet", @@ -49,9 +52,6 @@ full = [ "wasm-ext-websocket", "websocket", "yamux", - "onion", - "onion-native-tls", - "onion-rustls", ] autonat = ["dep:libp2p-autonat"] dcutr = ["dep:libp2p-dcutr", "libp2p-metrics?/dcutr"] From c09fa8064802c05ba1cf3d971cbac000d3946277 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Thu, 20 Oct 2022 10:43:56 +1100 Subject: [PATCH 42/47] Alphabetically sort dependency list --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index fd97642b2a2..979301a4129 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -134,9 +134,9 @@ smallvec = "1.6.1" libp2p-deflate = { version = "0.37.0", path = "transports/deflate", optional = true } libp2p-dns = { version = "0.37.0", path = "transports/dns", optional = true } libp2p-mdns = { version = "0.41.0", path = "protocols/mdns", optional = true } +libp2p-onion = { version = "0.1.0", path = "transports/onion", default-features = false, optional = true } libp2p-tcp = { version = "0.37.0", path = "transports/tcp", optional = true } libp2p-websocket = { version = "0.39.0", path = "transports/websocket", optional = true } -libp2p-onion = { version = "0.1.0", path = "transports/onion", default-features = false, optional = true } [target.'cfg(not(target_os = "unknown"))'.dependencies] libp2p-gossipsub = { version = "0.42.1", path = "protocols/gossipsub", optional = true } From 285bdc08c7b920b3fc2b39a89fd33df5a1bc8cad Mon Sep 17 00:00:00 2001 From: Hannes <55623006+umgefahren@users.noreply.github.com> Date: Thu, 20 Oct 2022 23:24:31 +0200 Subject: [PATCH 43/47] Update transports/onion/Cargo.toml Co-authored-by: Max Inden --- transports/onion/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transports/onion/Cargo.toml b/transports/onion/Cargo.toml index 48a2f67e30b..c8c42ece999 100644 --- a/transports/onion/Cargo.toml +++ b/transports/onion/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libp2p-onion" -version = "0.1.0" +version = "0.1.0-alpha" edition = "2021" license = "MIT" resolver = "2" From d2c62560715a9750b0dc761e38829a7e7e02674d Mon Sep 17 00:00:00 2001 From: Hannes <55623006+umgefahren@users.noreply.github.com> Date: Thu, 20 Oct 2022 22:56:24 +0000 Subject: [PATCH 44/47] Changed to alpha version. --- Cargo.toml | 2 +- transports/onion/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index da6613b56ff..28cdbbda4e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -122,7 +122,7 @@ smallvec = "1.6.1" libp2p-deflate = { version = "0.37.0", path = "transports/deflate", optional = true } libp2p-dns = { version = "0.37.0", path = "transports/dns", optional = true } libp2p-mdns = { version = "0.41.0", path = "protocols/mdns", optional = true } -libp2p-onion = { version = "0.1.0", path = "transports/onion", default-features = false, optional = true } +libp2p-onion = { version = "0.1.0-alpha", path = "transports/onion", default-features = false, optional = true } libp2p-tcp = { version = "0.37.0", path = "transports/tcp", optional = true } libp2p-websocket = { version = "0.39.0", path = "transports/websocket", optional = true } diff --git a/transports/onion/Cargo.toml b/transports/onion/Cargo.toml index c8c42ece999..9443f9e8774 100644 --- a/transports/onion/Cargo.toml +++ b/transports/onion/Cargo.toml @@ -15,7 +15,7 @@ tokio-crate = { package = "tokio", version = "1", optional = true, default-featu tor-rtcompat = "0.7" [dev-dependencies] -libp2p = { version = "0.49", path = "../../" } +libp2p = { version = "0.50", path = "../../" } tokio-crate = { package = "tokio", version = "1", features = ["rt", "macros"] } async-std-crate = { package = "async-std", version = "1", features = ["attributes"] } From 7865af755a5ebf0c2567c06e7ac9edd615a68468 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 25 Oct 2022 11:23:23 +0200 Subject: [PATCH 45/47] Fixed issue with Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4ae297fdd6b..3fefd73c9cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -164,7 +164,7 @@ members = [ "transports/deflate", "transports/dns", "transports/noise", - "transports/onion" + "transports/onion", "transports/plaintext", "transports/pnet", "transports/tcp", From dd5b8b7566ec379e903989e01c8a9898db83e3a9 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 25 Oct 2022 11:43:35 +0200 Subject: [PATCH 46/47] Added type alias for DataStream and removed custom doc(cfg( in order to comply to the new doc_auto_cfg policy --- transports/onion/src/lib.rs | 27 +++++++++++---------------- transports/onion/src/provider.rs | 18 ++++++++---------- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/transports/onion/src/lib.rs b/transports/onion/src/lib.rs index cb1ebcca73a..e2adf239a76 100644 --- a/transports/onion/src/lib.rs +++ b/transports/onion/src/lib.rs @@ -20,7 +20,7 @@ #![doc(html_logo_url = "https://libp2p.io/img/logo_small.png")] #![doc(html_favicon_url = "https://libp2p.io/img/favicon.png")] -#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] //! Tor based transport for libp2p. Connect through the Tor network to TCP listeners. //! //! Main entrypoint of the crate: [`OnionTransport`] @@ -40,7 +40,7 @@ //! ``` use address::{dangerous_extract_tor_address, safe_extract_tor_address}; -use arti_client::{DataStream, TorClient, TorClientBuilder}; +use arti_client::{TorClient, TorClientBuilder}; use futures::{future::BoxFuture, FutureExt}; use libp2p_core::{transport::TransportError, Multiaddr, Transport}; use provider::OnionStream; @@ -52,9 +52,12 @@ mod address; mod provider; #[cfg(feature = "tokio")] -#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] #[doc(inline)] -pub use provider::OnionTokioStream; +pub use provider::TokioOnionStream; + +#[cfg(feature = "async-std")] +#[doc(inline)] +pub use provider::AsyncStdOnionStream; pub type OnionError = arti_client::Error; @@ -118,7 +121,6 @@ macro_rules! default_constructor { } #[cfg(all(feature = "native-tls", feature = "async-std"))] -#[cfg_attr(docsrs, doc(cfg(all(feature = "native-tls", feature = "async-std"))))] impl OnionTransport { pub fn builder() -> OnionBuilder { let runtime = tor_rtcompat::async_std::AsyncStdNativeTlsRuntime::current() @@ -129,7 +131,6 @@ impl OnionTransport { } #[cfg(all(feature = "rustls", feature = "async-std"))] -#[cfg_attr(docsrs, doc(cfg(all(feature = "rustls", feature = "async-std"))))] impl OnionTransport { pub fn builder() -> OnionBuilder { let runtime = tor_rtcompat::async_std::AsyncStdRustlsRuntime::current() @@ -140,7 +141,6 @@ impl OnionTransport { } #[cfg(all(feature = "native-tls", feature = "tokio"))] -#[cfg_attr(docsrs, doc(cfg(all(feature = "native-tls", feature = "tokio"))))] impl OnionTransport { pub fn builder() -> OnionBuilder { let runtime = tor_rtcompat::tokio::TokioNativeTlsRuntime::current() @@ -151,7 +151,6 @@ impl OnionTransport { } #[cfg(all(feature = "rustls", feature = "tokio"))] -#[cfg_attr(docsrs, doc(cfg(all(feature = "rustls", feature = "tokio"))))] impl OnionTransport { pub fn builder() -> OnionBuilder { let runtime = tor_rtcompat::tokio::TokioRustlsRuntime::current() @@ -162,21 +161,17 @@ impl OnionTransport { } #[cfg(all(feature = "native-tls", feature = "async-std"))] -#[cfg_attr(docsrs, doc(cfg(all(feature = "native-tls", feature = "async-std"))))] pub type AsyncStdNativeTlsOnionTransport = - OnionTransport; + OnionTransport; #[cfg(all(feature = "rustls", feature = "async-std"))] -#[cfg_attr(docsrs, doc(cfg(all(feature = "rustls", feature = "async-std"))))] pub type AsyncStdRustlsOnionTransport = - OnionTransport; + OnionTransport; #[cfg(all(feature = "native-tls", feature = "tokio"))] -#[cfg_attr(docsrs, doc(cfg(all(feature = "native-tls", feature = "tokio"))))] pub type TokioNativeTlsOnionTransport = - OnionTransport; + OnionTransport; #[cfg(all(feature = "rustls", feature = "tokio"))] -#[cfg_attr(docsrs, doc(cfg(all(feature = "rustls", feature = "tokio"))))] pub type TokioRustlsOnionTransport = - OnionTransport; + OnionTransport; #[derive(Debug, Clone, Copy, Default)] pub struct AlwaysErrorListenerUpgrade(PhantomData); diff --git a/transports/onion/src/provider.rs b/transports/onion/src/provider.rs index c0b75afa235..841cb79ef02 100644 --- a/transports/onion/src/provider.rs +++ b/transports/onion/src/provider.rs @@ -25,28 +25,27 @@ pub trait OnionStream: AsyncRead + AsyncWrite + From {} impl OnionStream for DataStream {} +#[cfg(feature = "async-std")] +pub type AsyncStdOnionStream = DataStream; + #[cfg(feature = "tokio")] -#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] #[derive(Debug)] -pub struct OnionTokioStream { +pub struct TokioOnionStream { inner: DataStream, } #[cfg(feature = "tokio")] -#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] -impl From for OnionTokioStream { +impl From for TokioOnionStream { fn from(inner: DataStream) -> Self { Self { inner } } } #[cfg(feature = "tokio")] -#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] -impl OnionStream for OnionTokioStream {} +impl OnionStream for TokioOnionStream {} #[cfg(feature = "tokio")] -#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] -impl AsyncRead for OnionTokioStream { +impl AsyncRead for TokioOnionStream { fn poll_read( mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, @@ -63,8 +62,7 @@ impl AsyncRead for OnionTokioStream { } #[cfg(feature = "tokio")] -#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] -impl AsyncWrite for OnionTokioStream { +impl AsyncWrite for TokioOnionStream { #[inline] fn poll_write( mut self: std::pin::Pin<&mut Self>, From 4f72d3c2611cc8b8865ea253c12f118c612edf68 Mon Sep 17 00:00:00 2001 From: umgefahren <55623006+umgefahren@users.noreply.github.com> Date: Tue, 22 Nov 2022 19:21:53 +0100 Subject: [PATCH 47/47] Made compileable again --- transports/onion/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transports/onion/Cargo.toml b/transports/onion/Cargo.toml index 9443f9e8774..183e04b5353 100644 --- a/transports/onion/Cargo.toml +++ b/transports/onion/Cargo.toml @@ -9,7 +9,7 @@ resolver = "2" arti-client = { version = "0.7", default-features = false } async-std-crate = { package = "async-std", version = "1", optional = true, default-features = false } futures = "0.3" -libp2p-core = { version = "0.37", path = "../../core" } +libp2p-core = { version = "0.38", path = "../../core" } thiserror = "1" tokio-crate = { package = "tokio", version = "1", optional = true, default-features = false } tor-rtcompat = "0.7"