cargo: Add feature for Tor support
This commit is contained in:
parent
8bac24d6ab
commit
1e132685bc
|
|
@ -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"] }
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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<G: Ecdh<Pk = NodeId>>(
|
|||
(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.
|
||||
|
|
|
|||
|
|
@ -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"] }
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Self, Error> {
|
||||
let bytes: [u8; tor::ONION_V3_RAW_LEN] = Decode::decode(buf)?;
|
||||
|
|
|
|||
|
|
@ -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<u8> 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)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -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"] }
|
||||
|
|
|
|||
|
|
@ -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::<String>();
|
||||
|
|
|
|||
|
|
@ -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<u8> for AddressType {
|
|||
1 => Ok(AddressType::Ipv4),
|
||||
2 => Ok(AddressType::Ipv6),
|
||||
3 => Ok(AddressType::Dns),
|
||||
#[cfg(feature = "tor")]
|
||||
4 => Ok(AddressType::Onion),
|
||||
_ => Err(other),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<net::SocketAddr>,
|
||||
/// Onion address config.
|
||||
#[cfg(feature = "tor")]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub onion: Option<AddressConfig>,
|
||||
/// 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(),
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Reference in New Issue