diff --git a/crates/radicle-cli/Cargo.toml b/crates/radicle-cli/Cargo.toml index a85a9953..9634abb2 100644 --- a/crates/radicle-cli/Cargo.toml +++ b/crates/radicle-cli/Cargo.toml @@ -13,6 +13,10 @@ rust-version.workspace = true name = "rad" path = "src/main.rs" +[features] +default = ["tor"] +tor = ["radicle/tor"] + [dependencies] anyhow = "1" chrono = { workspace = true, features = ["clock", "std"] } diff --git a/crates/radicle-node/Cargo.toml b/crates/radicle-node/Cargo.toml index 6c86b28f..1b75d6f2 100644 --- a/crates/radicle-node/Cargo.toml +++ b/crates/radicle-node/Cargo.toml @@ -10,9 +10,10 @@ build = "build.rs" rust-version.workspace = true [features] -default = ["backtrace", "systemd", "structured-logger", "socket2"] +default = ["backtrace", "systemd", "structured-logger", "socket2", "tor"] systemd = ["dep:radicle-systemd"] test = ["radicle/test", "radicle-crypto/test", "radicle-crypto/cyphernet", "radicle-protocol/test", "qcheck", "snapbox"] +tor = ["cyphernet/tor", "radicle/tor", "radicle-protocol/tor"] [dependencies] backtrace = { version = "0.3.75", optional = true } @@ -21,7 +22,7 @@ bytes = { workspace = true } chrono = { workspace = true, features = ["clock"] } colored = { workspace = true } crossbeam-channel = { workspace = true } -cyphernet = { workspace = true, features = ["tor", "dns", "ed25519", "p2p-ed25519", "noise-framework", "noise_sha2"] } +cyphernet = { workspace = true, features = ["dns", "ed25519", "p2p-ed25519", "noise-framework", "noise_sha2"] } fastrand = { workspace = true } gix-packetline = { workspace = true, features = ["blocking-io"] } lexopt = { workspace = true } diff --git a/crates/radicle-node/src/wire.rs b/crates/radicle-node/src/wire.rs index 21f6dbf9..383df8bf 100644 --- a/crates/radicle-node/src/wire.rs +++ b/crates/radicle-node/src/wire.rs @@ -21,6 +21,7 @@ use radicle::collections::{RandomMap, RandomSet}; use radicle::crypto; use radicle::node::Link; use radicle::node::NodeId; +#[cfg(feature = "tor")] use radicle::node::config::AddressConfig; use radicle::storage::WriteStorage; use radicle_protocol::deserializer::Deserializer; @@ -1091,6 +1092,7 @@ pub fn dial>( (HostName::Dns(_), Some(proxy)) => proxy.into(), (HostName::Dns(dns), None) => NetAddr::new(InetHost::Dns(dns.clone()), remote_addr.port), // For onion addresses, handle with care. + #[cfg(feature = "tor")] (HostName::Tor(onion), proxy) => match config.onion { // In onion proxy mode, simply use the configured proxy address. // This takes precedence over any global proxy. diff --git a/crates/radicle-protocol/Cargo.toml b/crates/radicle-protocol/Cargo.toml index 1642f90e..149ee70b 100644 --- a/crates/radicle-protocol/Cargo.toml +++ b/crates/radicle-protocol/Cargo.toml @@ -10,12 +10,13 @@ rust-version.workspace = true [features] test = ["radicle/test", "radicle-crypto/test", "radicle-crypto/cyphernet", "qcheck"] +tor = ["cypheraddr/tor", "radicle/tor"] [dependencies] bloomy = "1.2" bytes = { workspace = true } crossbeam-channel = { workspace = true } -cypheraddr = { workspace = true, features = ["serde", "tor"] } +cypheraddr = { workspace = true, features = ["serde"] } fastrand = { workspace = true } log = { workspace = true, features = ["std"] } nonempty = { workspace = true, features = ["serialize"] } diff --git a/crates/radicle-protocol/src/service.rs b/crates/radicle-protocol/src/service.rs index 0bf6b405..dee16ea7 100644 --- a/crates/radicle-protocol/src/service.rs +++ b/crates/radicle-protocol/src/service.rs @@ -2655,6 +2655,7 @@ where fn is_supported_address(&self, address: &Address) -> bool { match AddressType::from(address) { // Only consider onion addresses if configured. + #[cfg(feature = "tor")] AddressType::Onion => self.config.onion.is_some(), AddressType::Dns | AddressType::Ipv4 | AddressType::Ipv6 => true, } diff --git a/crates/radicle-protocol/src/wire.rs b/crates/radicle-protocol/src/wire.rs index 6d5aea8a..bed60762 100644 --- a/crates/radicle-protocol/src/wire.rs +++ b/crates/radicle-protocol/src/wire.rs @@ -14,6 +14,7 @@ use std::string::FromUtf8Error; use bytes::{Buf, BufMut}; +#[cfg(feature = "tor")] use cypheraddr::tor; use radicle::crypto::{PublicKey, Signature}; @@ -56,6 +57,7 @@ pub enum Invalid { Alias(#[from] node::AliasError), #[error("invalid user agent string: {err}")] InvalidUserAgent { err: String }, + #[cfg(feature = "tor")] #[error("invalid onion address: {0}")] OnionAddr(#[from] tor::OnionAddrDecodeError), #[error("invalid timestamp: {actual_millis} millis")] @@ -257,6 +259,7 @@ impl Encode for Refs { } } +#[cfg(feature = "tor")] impl Encode for cypheraddr::tor::OnionAddrV3 { fn encode(&self, buf: &mut impl BufMut) { self.into_raw_bytes().encode(buf) @@ -518,6 +521,7 @@ impl Decode for node::Features { } } +#[cfg(feature = "tor")] impl Decode for tor::OnionAddrV3 { fn decode(buf: &mut impl Buf) -> Result { let bytes: [u8; tor::ONION_V3_RAW_LEN] = Decode::decode(buf)?; diff --git a/crates/radicle-protocol/src/wire/message.rs b/crates/radicle-protocol/src/wire/message.rs index e8520949..70ab38a7 100644 --- a/crates/radicle-protocol/src/wire/message.rs +++ b/crates/radicle-protocol/src/wire/message.rs @@ -2,7 +2,9 @@ use std::{mem, net}; use bytes::Buf; use bytes::BufMut; -use cypheraddr::{HostName, NetAddr, tor}; +#[cfg(feature = "tor")] +use cypheraddr::tor; +use cypheraddr::{HostName, NetAddr}; use radicle::crypto::Signature; use radicle::git::Oid; use radicle::identity::RepoId; @@ -79,6 +81,7 @@ pub enum AddressType { Ipv4 = 1, Ipv6 = 2, Dns = 3, + #[cfg(feature = "tor")] Onion = 4, } @@ -94,6 +97,7 @@ impl From<&Address> for AddressType { HostName::Ip(net::IpAddr::V4(_)) => AddressType::Ipv4, HostName::Ip(net::IpAddr::V6(_)) => AddressType::Ipv6, HostName::Dns(_) => AddressType::Dns, + #[cfg(feature = "tor")] HostName::Tor(_) => AddressType::Onion, _ => todo!(), // FIXME(cloudhead): Maxim will remove `non-exhaustive` } @@ -108,6 +112,7 @@ impl TryFrom for AddressType { 1 => Ok(AddressType::Ipv4), 2 => Ok(AddressType::Ipv6), 3 => Ok(AddressType::Dns), + #[cfg(feature = "tor")] 4 => Ok(AddressType::Onion), _ => Err(other), } @@ -356,6 +361,7 @@ impl wire::Encode for Address { u8::from(AddressType::Dns).encode(buf); dns.encode(buf); } + #[cfg(feature = "tor")] HostName::Tor(addr) => { u8::from(AddressType::Onion).encode(buf); addr.encode(buf); @@ -393,6 +399,7 @@ impl wire::Decode for Address { HostName::Dns(dns) } + #[cfg(feature = "tor")] Ok(AddressType::Onion) => { let onion: tor::OnionAddrV3 = wire::Decode::decode(buf)?; diff --git a/crates/radicle/Cargo.toml b/crates/radicle/Cargo.toml index b84ca436..850adb80 100644 --- a/crates/radicle/Cargo.toml +++ b/crates/radicle/Cargo.toml @@ -24,6 +24,7 @@ schemars = [ "radicle-localtime/schemars", "dep:schemars" ] +tor = ["cyphernet/tor"] [dependencies] amplify = { workspace = true, features = ["std"] } @@ -32,7 +33,7 @@ bytesize = { version = "2", features = ["serde"] } chrono = { workspace = true, features = ["clock"], optional = true } colored = { workspace = true, optional = true } crossbeam-channel = { workspace = true } -cyphernet = { workspace = true, features = ["tor", "dns", "p2p-ed25519"] } +cyphernet = { workspace = true, features = ["dns", "p2p-ed25519"] } dunce = { workspace = true } fast-glob = { version = "0.3.2" } fastrand = { workspace = true, features = ["std"] } diff --git a/crates/radicle/src/node.rs b/crates/radicle/src/node.rs index e313e94d..25f344b2 100644 --- a/crates/radicle/src/node.rs +++ b/crates/radicle/src/node.rs @@ -476,6 +476,7 @@ impl Address { } /// Returns `true` if the [`HostName`] is a Tor onion address. + #[cfg(feature = "tor")] pub fn is_onion(&self) -> bool { match self.0.host { HostName::Tor(_) => true, @@ -493,6 +494,7 @@ impl Address { HostName::Ip(IpAddr::V4(ip)) => ip.to_string(), HostName::Ip(IpAddr::V6(ip)) => format!("[{ip}]"), HostName::Dns(dns) => dns.clone(), + #[cfg(feature = "tor")] HostName::Tor(onion) => { let onion = onion.to_string(); let start = onion.chars().take(8).collect::(); diff --git a/crates/radicle/src/node/address.rs b/crates/radicle/src/node/address.rs index baefd98f..92a0227c 100644 --- a/crates/radicle/src/node/address.rs +++ b/crates/radicle/src/node/address.rs @@ -201,6 +201,7 @@ pub enum AddressType { Ipv4 = 1, Ipv6 = 2, Dns = 3, + #[cfg(feature = "tor")] Onion = 4, } @@ -216,6 +217,7 @@ impl From<&Address> for AddressType { HostName::Ip(net::IpAddr::V4(_)) => AddressType::Ipv4, HostName::Ip(net::IpAddr::V6(_)) => AddressType::Ipv6, HostName::Dns(_) => AddressType::Dns, + #[cfg(feature = "tor")] HostName::Tor(_) => AddressType::Onion, _ => todo!(), // FIXME(cloudhead): Maxim will remove `non-exhaustive` } @@ -230,6 +232,7 @@ impl TryFrom for AddressType { 1 => Ok(AddressType::Ipv4), 2 => Ok(AddressType::Ipv6), 3 => Ok(AddressType::Dns), + #[cfg(feature = "tor")] 4 => Ok(AddressType::Onion), _ => Err(other), } diff --git a/crates/radicle/src/node/address/store.rs b/crates/radicle/src/node/address/store.rs index 8a683385..fd566e12 100644 --- a/crates/radicle/src/node/address/store.rs +++ b/crates/radicle/src/node/address/store.rs @@ -535,6 +535,7 @@ impl TryFrom<&sql::Value> for AddressType { "ipv4" => Ok(AddressType::Ipv4), "ipv6" => Ok(AddressType::Ipv6), "dns" => Ok(AddressType::Dns), + #[cfg(feature = "tor")] "onion" => Ok(AddressType::Onion), _ => Err(err), }, @@ -549,6 +550,7 @@ impl sql::BindableWithIndex for AddressType { Self::Ipv4 => "ipv4".bind(stmt, i), Self::Ipv6 => "ipv6".bind(stmt, i), Self::Dns => "dns".bind(stmt, i), + #[cfg(feature = "tor")] Self::Onion => "onion".bind(stmt, i), } } diff --git a/crates/radicle/src/node/config.rs b/crates/radicle/src/node/config.rs index dfdaa886..600151e0 100644 --- a/crates/radicle/src/node/config.rs +++ b/crates/radicle/src/node/config.rs @@ -22,7 +22,9 @@ pub type ProtocolVersion = u8; pub mod seeds { use std::{str::FromStr, sync::LazyLock}; - use cyphernet::addr::{HostName, NetAddr, tor::OnionAddrV3}; + #[cfg(feature = "tor")] + use cyphernet::addr::tor::OnionAddrV3; + use cyphernet::addr::{HostName, NetAddr}; use super::{ConnectAddress, NodeId, PeerAddr}; @@ -41,6 +43,7 @@ pub mod seeds { NodeId::from_str("z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7").unwrap(), vec![ HostName::Dns("iris.radicle.xyz".to_owned()), + #[cfg(feature = "tor")] #[allow(clippy::unwrap_used)] // Value is manually verified. OnionAddrV3::from_str( "irisradizskwweumpydlj4oammoshkxxjur3ztcmo7cou5emc6s5lfid.onion", @@ -58,6 +61,7 @@ pub mod seeds { NodeId::from_str("z6Mkmqogy2qEM2ummccUthFEaaHvyYmYBYh3dbe9W4ebScxo").unwrap(), vec![ HostName::Dns("rosa.radicle.xyz".to_owned()), + #[cfg(feature = "tor")] #[allow(clippy::unwrap_used)] // Value is manually verified. OnionAddrV3::from_str( "rosarad5bxgdlgjnzzjygnsxrwxmoaj4vn7xinlstwglxvyt64jlnhyd.onion", @@ -354,6 +358,7 @@ pub enum Relay { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase", tag = "mode")] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +#[cfg(feature = "tor")] pub enum AddressConfig { /// Proxy connections to this address type. Proxy { @@ -540,6 +545,7 @@ pub struct Config { #[serde(default, skip_serializing_if = "Option::is_none")] pub proxy: Option, /// Onion address config. + #[cfg(feature = "tor")] #[serde(default, skip_serializing_if = "Option::is_none")] pub onion: Option, /// Peer-to-peer network. @@ -596,6 +602,7 @@ impl Config { external_addresses: vec![], network: Network::default(), proxy: None, + #[cfg(feature = "tor")] onion: None, relay: Relay::default(), limits: Limits::default(), diff --git a/crates/radicle/src/test/arbitrary.rs b/crates/radicle/src/test/arbitrary.rs index 3d356dc8..f77e300c 100644 --- a/crates/radicle/src/test/arbitrary.rs +++ b/crates/radicle/src/test/arbitrary.rs @@ -5,8 +5,8 @@ use std::str::FromStr; use std::{iter, net}; use crypto::PublicKey; -use cyphernet::EcPk; -use cyphernet::addr::tor::OnionAddrV3; +#[cfg(feature = "tor")] +use cyphernet::{EcPk, addr::tor::OnionAddrV3}; use qcheck::Arbitrary; use crate::identity::doc::Visibility; @@ -229,6 +229,7 @@ impl Arbitrary for Address { .unwrap() .to_string(), ), + #[cfg(feature = "tor")] AddressType::Onion => { let pk = PublicKey::arbitrary(g); let addr = OnionAddrV3::from(