node: Support "transparent" Tor
This allows for configuring your node such that `.onion` addresses encountered are treated as regular DNS names.
This commit is contained in:
parent
70d2e1a0db
commit
deeb39c558
|
|
@ -26,6 +26,7 @@ $ rad config
|
|||
},
|
||||
"connect": [],
|
||||
"externalAddresses": [],
|
||||
"tor": null,
|
||||
"network": "main",
|
||||
"relay": true,
|
||||
"limits": {
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ mod routes {
|
|||
},
|
||||
"connect": [],
|
||||
"externalAddresses": [],
|
||||
"tor": null,
|
||||
"network": "main",
|
||||
"relay": true,
|
||||
"limits": {
|
||||
|
|
|
|||
|
|
@ -115,7 +115,6 @@ fn execute() -> anyhow::Result<()> {
|
|||
// Add the preferred seeds as persistent peers so that we reconnect to them automatically.
|
||||
config.node.connect.extend(config.preferred_seeds);
|
||||
|
||||
let proxy = net::SocketAddr::new(net::Ipv4Addr::LOCALHOST.into(), 9050);
|
||||
let listen: Vec<std::net::SocketAddr> = if !options.listen.is_empty() {
|
||||
options.listen.clone()
|
||||
} else {
|
||||
|
|
@ -133,7 +132,7 @@ fn execute() -> anyhow::Result<()> {
|
|||
log::debug!(target: "node", "Removing existing control socket..");
|
||||
fs::remove_file(home.socket()).ok();
|
||||
}
|
||||
Runtime::init(home, config.node, listen, proxy, signals, signer)?.run()?;
|
||||
Runtime::init(home, config.node, listen, signals, signer)?.run()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,7 +139,6 @@ impl Runtime {
|
|||
home: Home,
|
||||
config: service::Config,
|
||||
listen: Vec<net::SocketAddr>,
|
||||
proxy: net::SocketAddr,
|
||||
signals: chan::Receiver<()>,
|
||||
signer: G,
|
||||
) -> Result<Runtime, Error>
|
||||
|
|
@ -238,7 +237,7 @@ impl Runtime {
|
|||
service.initialize(clock)?;
|
||||
|
||||
let (worker_send, worker_recv) = chan::unbounded::<worker::Task>();
|
||||
let mut wire = Wire::new(service, worker_send, signer.clone(), proxy);
|
||||
let mut wire = Wire::new(service, worker_send, signer.clone());
|
||||
let mut local_addrs = Vec::new();
|
||||
|
||||
for addr in listen {
|
||||
|
|
|
|||
|
|
@ -552,6 +552,11 @@ where
|
|||
&mut self.outbox
|
||||
}
|
||||
|
||||
/// Get configuration.
|
||||
pub fn config(&self) -> &Config {
|
||||
&self.config
|
||||
}
|
||||
|
||||
/// Lookup a repository, both locally and in the routing table.
|
||||
pub fn lookup(&self, rid: RepoId) -> Result<Lookup, LookupError> {
|
||||
let remote = self.db.routing().get(&rid)?.iter().cloned().collect();
|
||||
|
|
|
|||
|
|
@ -492,13 +492,11 @@ impl<G: cyphernet::Ecdh<Pk = NodeId> + Signer + Clone> Node<G> {
|
|||
/// Spawn a node in its own thread.
|
||||
pub fn spawn(self) -> NodeHandle<G> {
|
||||
let listen = vec![([0, 0, 0, 0], 0).into()];
|
||||
let proxy = net::SocketAddr::new(net::Ipv4Addr::LOCALHOST.into(), 9050);
|
||||
let (_, signals) = chan::bounded(1);
|
||||
let rt = Runtime::init(
|
||||
self.home.clone(),
|
||||
self.config,
|
||||
listen,
|
||||
proxy,
|
||||
signals,
|
||||
self.signer.clone(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ use netservices::{NetConnection, NetProtocol, NetReader, NetWriter};
|
|||
use reactor::{ResourceId, ResourceType, Timestamp};
|
||||
|
||||
use radicle::collections::RandomMap;
|
||||
use radicle::node::config::TorConfig;
|
||||
use radicle::node::NodeId;
|
||||
use radicle::storage::WriteStorage;
|
||||
|
||||
|
|
@ -318,8 +319,6 @@ pub struct Wire<D, S, G: Signer + Ecdh> {
|
|||
listening: RandomMap<RawFd, net::SocketAddr>,
|
||||
/// Peer (established) sessions.
|
||||
peers: Peers,
|
||||
/// SOCKS5 proxy address.
|
||||
proxy: net::SocketAddr,
|
||||
}
|
||||
|
||||
impl<D, S, G> Wire<D, S, G>
|
||||
|
|
@ -328,19 +327,13 @@ where
|
|||
S: WriteStorage + 'static,
|
||||
G: Signer + Ecdh<Pk = NodeId>,
|
||||
{
|
||||
pub fn new(
|
||||
service: Service<D, S, G>,
|
||||
worker: chan::Sender<Task>,
|
||||
signer: G,
|
||||
proxy: net::SocketAddr,
|
||||
) -> Self {
|
||||
pub fn new(service: Service<D, S, G>, worker: chan::Sender<Task>, signer: G) -> Self {
|
||||
assert!(service.started().is_some(), "Service must be initialized");
|
||||
|
||||
Self {
|
||||
service,
|
||||
worker,
|
||||
signer,
|
||||
proxy,
|
||||
actions: VecDeque::new(),
|
||||
inbound: RandomMap::default(),
|
||||
outbound: RandomMap::default(),
|
||||
|
|
@ -954,8 +947,7 @@ where
|
|||
addr.to_inner(),
|
||||
node_id,
|
||||
self.signer.clone(),
|
||||
self.proxy.into(),
|
||||
false,
|
||||
self.service.config(),
|
||||
)
|
||||
.and_then(|session| {
|
||||
NetTransport::<WireSession<G>>::with_session(session, Link::Outbound)
|
||||
|
|
@ -1057,19 +1049,46 @@ pub fn dial<G: Signer + Ecdh<Pk = NodeId>>(
|
|||
remote_addr: NetAddr<HostName>,
|
||||
remote_id: <G as EcSk>::Pk,
|
||||
signer: G,
|
||||
proxy_addr: NetAddr<InetHost>,
|
||||
force_proxy: bool,
|
||||
config: &service::Config,
|
||||
) -> io::Result<WireSession<G>> {
|
||||
let connection = if force_proxy {
|
||||
// Convert the remote address into an internet protocol address (IP or DNS).
|
||||
let inet_addr: NetAddr<InetHost> = match config.tor {
|
||||
// In Tor proxy mode, simply specify a different connection address.
|
||||
Some(TorConfig::Proxy { address: proxy }) => remote_addr.connection_addr(proxy.into()),
|
||||
// In transparent Tor mode, we treat `.onion` addresses as regular DNS names.
|
||||
Some(TorConfig::Transparent) => {
|
||||
let host = match &remote_addr.host {
|
||||
HostName::Ip(ip) => InetHost::Ip(*ip),
|
||||
HostName::Dns(dns) => InetHost::Dns(dns.clone()),
|
||||
HostName::Tor(onion) => InetHost::Dns(onion.to_string()),
|
||||
_ => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Unsupported,
|
||||
"unsupported remote address type",
|
||||
))
|
||||
}
|
||||
};
|
||||
NetAddr::new(host, remote_addr.port)
|
||||
}
|
||||
// Without any Tor configuration, refuse to connect to a `.onion` address.
|
||||
None => {
|
||||
let host = match &remote_addr.host {
|
||||
HostName::Ip(ip) => InetHost::Ip(*ip),
|
||||
HostName::Dns(dns) => InetHost::Dns(dns.clone()),
|
||||
_ => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Unsupported,
|
||||
"unsupported remote address type",
|
||||
))
|
||||
}
|
||||
};
|
||||
NetAddr::new(host, remote_addr.port)
|
||||
}
|
||||
};
|
||||
// Nb. This timeout is currently not used by the underlying library due to the
|
||||
// `socket2` library not supporting non-blocking connect with timeout.
|
||||
net::TcpStream::connect_nonblocking(proxy_addr, DEFAULT_DIAL_TIMEOUT)?
|
||||
} else {
|
||||
net::TcpStream::connect_nonblocking(
|
||||
remote_addr.connection_addr(proxy_addr),
|
||||
DEFAULT_DIAL_TIMEOUT,
|
||||
)?
|
||||
};
|
||||
let connection = net::TcpStream::connect_nonblocking(inet_addr, DEFAULT_DIAL_TIMEOUT)?;
|
||||
|
||||
connection.set_read_timeout(Some(DEFAULT_CONNECTION_TIMEOUT))?;
|
||||
connection.set_write_timeout(Some(DEFAULT_CONNECTION_TIMEOUT))?;
|
||||
|
||||
|
|
@ -1078,7 +1097,6 @@ pub fn dial<G: Signer + Ecdh<Pk = NodeId>>(
|
|||
Some(remote_id),
|
||||
connection,
|
||||
signer,
|
||||
force_proxy,
|
||||
))
|
||||
}
|
||||
|
||||
|
|
@ -1088,7 +1106,7 @@ pub fn accept<G: Signer + Ecdh<Pk = NodeId>>(
|
|||
connection: net::TcpStream,
|
||||
signer: G,
|
||||
) -> WireSession<G> {
|
||||
session::<G>(remote_addr, None, connection, signer, false)
|
||||
session::<G>(remote_addr, None, connection, signer)
|
||||
}
|
||||
|
||||
/// Create a new [`WireSession`].
|
||||
|
|
@ -1097,9 +1115,8 @@ fn session<G: Signer + Ecdh<Pk = NodeId>>(
|
|||
remote_id: Option<NodeId>,
|
||||
connection: net::TcpStream,
|
||||
signer: G,
|
||||
force_proxy: bool,
|
||||
) -> WireSession<G> {
|
||||
let socks5 = socks5::Socks5::with(remote_addr, force_proxy);
|
||||
let socks5 = socks5::Socks5::with(remote_addr, false);
|
||||
let proxy = Socks5Session::with(connection, socks5);
|
||||
let pair = G::generate_keypair();
|
||||
let keyset = Keyset {
|
||||
|
|
|
|||
|
|
@ -234,6 +234,19 @@ impl Default for PeerConfig {
|
|||
}
|
||||
}
|
||||
|
||||
/// Tor configuration.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase", tag = "mode")]
|
||||
pub enum TorConfig {
|
||||
/// Connect via SOCKS5 proxy.
|
||||
Proxy {
|
||||
/// Tor proxy address.
|
||||
address: net::SocketAddr,
|
||||
},
|
||||
/// Treat Tor onion addresses as DNS names.
|
||||
Transparent,
|
||||
}
|
||||
|
||||
/// Service configuration.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
|
@ -253,6 +266,9 @@ pub struct Config {
|
|||
/// Specify the node's public addresses
|
||||
#[serde(default)]
|
||||
pub external_addresses: Vec<Address>,
|
||||
/// Tor configuration.
|
||||
#[serde(default)]
|
||||
pub tor: Option<TorConfig>,
|
||||
/// Peer-to-peer network.
|
||||
#[serde(default)]
|
||||
pub network: Network,
|
||||
|
|
@ -289,6 +305,7 @@ impl Config {
|
|||
connect: HashSet::default(),
|
||||
external_addresses: vec![],
|
||||
network: Network::default(),
|
||||
tor: None,
|
||||
relay: true,
|
||||
limits: Limits::default(),
|
||||
workers: DEFAULT_WORKERS,
|
||||
|
|
|
|||
Loading…
Reference in New Issue