diff --git a/libp2p-tor/Cargo.toml b/libp2p-tor/Cargo.toml index d52402f9..b31a5334 100644 --- a/libp2p-tor/Cargo.toml +++ b/libp2p-tor/Cargo.toml @@ -1,28 +1,27 @@ [package] name = "libp2p-tor" version = "0.1.0" -authors = ["Thomas Eizinger "] +authors = [ "Thomas Eizinger " ] edition = "2018" - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] anyhow = "1" # TODO: Get rid of anyhow dependency -torut = { version = "0.1", default-features = false, features = ["v3", "control"] } -tokio-socks = "0.5" -libp2p = { version = "0.37", default-features = false, features = ["tcp-tokio"] } -tokio = { version = "1", features = ["sync"] } -futures = "0.3" -tracing = "0.1" -data-encoding = "2.3" -reqwest = { version = "0.11", features = ["socks", "rustls-tls"], default-features = false } async-trait = "0.1" +data-encoding = "2.3" +futures = "0.3" +libp2p = { version = "0.37", default-features = false, features = [ "tcp-tokio" ] } rand = { version = "0.8", optional = true } +reqwest = { version = "0.11", features = [ "socks", "rustls-tls" ], default-features = false } +tokio = { version = "1", features = [ "sync" ] } +tokio-socks = "0.5" +torut = { version = "0.1", default-features = false, features = [ "v3", "control" ] } +tracing = "0.1" [dev-dependencies] -tempfile = "3" -tokio = { version = "1", features = ["full"] } +libp2p = { version = "0.37", default-features = false, features = [ "yamux", "noise", "ping" ] } rand = "0.8" -libp2p = { version = "0.37", default-features = false, features = ["yamux", "noise", "ping"] } -tracing-subscriber = { version = "0.2", default-features = false, features = ["fmt", "ansi", "env-filter", "chrono", "tracing-log"] } +tempfile = "3" testcontainers = "0.12" +tokio = { version = "1", features = [ "full" ] } +tracing-subscriber = { version = "0.2", default-features = false, features = [ "fmt", "ansi", "env-filter", "chrono", "tracing-log" ] } diff --git a/libp2p-tor/examples/dialer.rs b/libp2p-tor/examples/dialer.rs index 738cbcf6..fca1b647 100644 --- a/libp2p-tor/examples/dialer.rs +++ b/libp2p-tor/examples/dialer.rs @@ -8,15 +8,19 @@ use std::time::Duration; #[tokio::main] async fn main() { - let addr_to_dial = std::env::args() - .next() - .unwrap() + let arg = std::env::args() + .last() + .unwrap(); + + let addr_to_dial = arg .parse::() .unwrap(); let mut swarm = new_swarm(); println!("Peer-ID: {}", swarm.local_peer_id()); + + println!("Dialing {}", addr_to_dial); swarm.dial_addr(addr_to_dial).unwrap(); loop { @@ -41,7 +45,9 @@ async fn main() { println!("Failed to ping {}: {}", peer, failure) } }, - _ => {} + event => { + println!("Swarm event: {:?}", event) + } } } } diff --git a/libp2p-tor/examples/listener.rs b/libp2p-tor/examples/listener.rs index c45c7c7e..79270b7c 100644 --- a/libp2p-tor/examples/listener.rs +++ b/libp2p-tor/examples/listener.rs @@ -2,7 +2,7 @@ use libp2p::core::muxing::StreamMuxerBox; use libp2p::core::upgrade::Version; use libp2p::ping::{Ping, PingEvent, PingSuccess}; use libp2p::swarm::{SwarmBuilder, SwarmEvent}; -use libp2p::{identity, noise, yamux, Swarm, Transport}; +use libp2p::{identity, noise, yamux, Swarm, Transport, Multiaddr}; use libp2p_tor::duplex; use libp2p_tor::torut_ext::AuthenticatedConnectionExt; use noise::NoiseConfig; @@ -10,23 +10,27 @@ use rand::Rng; use std::time::Duration; use torut::control::AuthenticatedConn; use torut::onion::TorSecretKeyV3; +use std::str::FromStr; #[tokio::main] async fn main() { - let wildcard_multiaddr = - "/onion3/WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW:8080" - .parse() - .unwrap(); - let mut swarm = new_swarm().await; + let key = random_onion_identity(); + let onion_address = key.public().get_onion_address().get_address_without_dot_onion(); + let onion_port = 7654; - println!("Peer-ID: {}", swarm.local_peer_id()); - swarm.listen_on(wildcard_multiaddr).unwrap(); + let mut swarm = new_swarm(key).await; + let peer_id = *swarm.local_peer_id(); + + println!("Peer-ID: {}", peer_id); + // TODO: Figure out what to with the port, we could also set it to 0 and then imply it from the assigned port + swarm.listen_on(Multiaddr::from_str(format!("/onion3/{}:{}", onion_address, onion_port).as_str()).unwrap()).unwrap(); loop { match swarm.next_event().await { SwarmEvent::NewListenAddr(addr) => { println!("Listening on {}", addr); + println!("Connection string: {}/p2p/{}", addr, peer_id) } SwarmEvent::ConnectionEstablished { peer_id, endpoint, .. @@ -48,7 +52,9 @@ async fn main() { println!("Failed to ping {}: {}", peer, failure) } }, - _ => {} + event => { + println!("Swarm event: {:?}", event) + } } } } @@ -58,13 +64,13 @@ async fn main() { /// /// In particular, this swarm can create ephemeral hidden services on the /// configured Tor node. -async fn new_swarm() -> Swarm { +async fn new_swarm(key: TorSecretKeyV3) -> Swarm { let identity = identity::Keypair::generate_ed25519(); SwarmBuilder::new( duplex::TorConfig::new( AuthenticatedConn::new(9051).await.unwrap(), - random_onion_identity, + key, ) .await .unwrap() diff --git a/libp2p-tor/src/duplex.rs b/libp2p-tor/src/duplex.rs index 3b9d3f9b..1c2752be 100644 --- a/libp2p-tor/src/duplex.rs +++ b/libp2p-tor/src/duplex.rs @@ -15,13 +15,6 @@ use tokio::sync::Mutex; use torut::control::{AsyncEvent, AuthenticatedConn}; use torut::onion::TorSecretKeyV3; -/// This is the hash of -/// `/onion3/WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW`. -const WILDCARD_ONION_ADDR_HASH: [u8; 35] = [ - 181, 173, 107, 90, 214, 181, 173, 107, 90, 214, 181, 173, 107, 90, 214, 181, 173, 107, 90, 214, - 181, 173, 107, 90, 214, 181, 173, 107, 90, 214, 181, 173, 107, 90, 214, -]; - type TorutAsyncEventHandler = fn( AsyncEvent<'_>, @@ -31,32 +24,33 @@ type TorutAsyncEventHandler = pub struct TorConfig { inner: MapErr, fn(std::io::Error) -> Error>, /* TODO: Make generic over async-std / tokio */ tor_client: Arc>>, - onion_key_generator: Arc TorSecretKeyV3) + Send + Sync>, + key: TorSecretKeyV3, socks_port: u16, } impl TorConfig { pub async fn new( mut client: AuthenticatedConn, - onion_key_generator: impl (Fn() -> TorSecretKeyV3) + Send + Sync + 'static, + // TODO: change to key directly + key: TorSecretKeyV3, ) -> Result { let socks_port = client.get_socks_port().await?; Ok(Self { inner: TokioTcpConfig::new().map_err(Error::InnerTransprot), tor_client: Arc::new(Mutex::new(client)), - onion_key_generator: Arc::new(onion_key_generator), + key, socks_port, }) } pub async fn from_control_port( control_port: u16, - key_generator: impl (Fn() -> TorSecretKeyV3) + Send + Sync + 'static, + key: TorSecretKeyV3, ) -> Result { let client = AuthenticatedConn::new(control_port).await?; - Self::new(client, key_generator).await + Self::new(client, key).await } } @@ -80,9 +74,9 @@ impl Transport for TorConfig { return Err(TransportError::MultiaddrNotSupported(addr)); }; - if onion.hash() != &WILDCARD_ONION_ADDR_HASH { - return Err(TransportError::Other(Error::OnlyWildcardAllowed)); - } + let key: TorSecretKeyV3 = self.key; + let onion_bytes = key.public().get_onion_address().get_raw_bytes(); + let onion_port = onion.port(); let localhost_tcp_random_port_addr = "/ip4/127.0.0.1/tcp/0" .parse() @@ -90,10 +84,6 @@ impl Transport for TorConfig { let listener = self.inner.listen_on(localhost_tcp_random_port_addr)?; - let key: TorSecretKeyV3 = (self.onion_key_generator)(); - let onion_bytes = key.public().get_onion_address().get_raw_bytes(); - let onion_port = onion.port(); - let tor_client = self.tor_client; let listener = listener @@ -115,6 +105,7 @@ impl Transport for TorConfig { }) .expect("TODO: Error handling"); + // TODO: Don't fully understand this part, why would we have two different multiaddresses here? the actual onion address and the multiaddress would make more sense...? tracing::debug!( "Setting up hidden service at {} to forward to {}", onion_multiaddress, @@ -125,6 +116,7 @@ impl Transport for TorConfig { .clone() .lock() .await + // TODO: Potentially simplify this, in our setup the onion port is always equal to the local port. Otherwise we would have the user provide an additional port for the oion service. .add_ephemeral_service(&key, onion_port, local_port) .await { @@ -141,7 +133,9 @@ impl Transport for TorConfig { local_addr, remote_addr, }, - ListenerEvent::AddressExpired(_) => { + // TODO: why was the constructed multiaddr used here? + ListenerEvent::AddressExpired(adr) => { + // TODO: even if so, why would we ignore it? Far more logical to just use it... // can ignore address because we only ever listened on one and we // know which one that was @@ -156,7 +150,7 @@ impl Transport for TorConfig { .del_onion(&onion_address_without_dot_onion) .await { - Ok(()) => ListenerEvent::AddressExpired(onion_multiaddress), + Ok(()) => ListenerEvent::AddressExpired(adr), Err(e) => ListenerEvent::Error(Error::Torut( torut_ext::Error::Connection(e), )), diff --git a/libp2p-tor/tests/integration_test.rs b/libp2p-tor/tests/integration_test.rs index 6478997d..5b758fcc 100644 --- a/libp2p-tor/tests/integration_test.rs +++ b/libp2p-tor/tests/integration_test.rs @@ -42,7 +42,7 @@ async fn create_ephemeral_service() { AuthenticatedConn::with_password(9051, "supersecret") .await .unwrap(), - move || onion_key_bytes.into(), + onion_key_bytes.into(), ) .await .unwrap() @@ -149,17 +149,16 @@ impl IntoIterator for TorArgs { let mut args = Vec::new(); if let Some(port) = self.socks_port { - args.push(format!("SocksPort")); + args.push("SocksPort".to_string()); args.push(format!("0.0.0.0:{}", port)); } if let Some(port) = self.control_port { - args.push(format!("ControlPort")); + args.push("ControlPort".to_string()); args.push(format!("0.0.0.0:{}", port)); - args.push(format!("HashedControlPassword")); - args.push(format!( - "16:436B425404AA332A60B4F341C2023146C4B3A80548D757F0BB10DE81B4" - )) + args.push("HashedControlPassword".to_string()); + args.push("16:436B425404AA332A60B4F341C2023146C4B3A80548D757F0BB10DE81B4" + .to_string()) } args.into_iter() @@ -186,7 +185,7 @@ impl Image for TorImage { } fn args(&self) -> Self::Args { - self.args.clone() + self.args } fn env_vars(&self) -> Self::EnvVars {