Skip to content

Commit 728b909

Browse files
committed
Add Tor support for outbound connections via SOCKS
1 parent 7ad0d63 commit 728b909

File tree

5 files changed

+177
-30
lines changed

5 files changed

+177
-30
lines changed

bindings/ldk_node.udl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dictionary Config {
1313
u64 probing_liquidity_limit_multiplier;
1414
AnchorChannelsConfig? anchor_channels_config;
1515
RouteParametersConfig? route_parameters;
16+
TorConfig? tor_config;
1617
};
1718

1819
dictionary AnchorChannelsConfig {
@@ -57,6 +58,10 @@ dictionary LSPS2ServiceConfig {
5758
boolean client_trusts_lsp;
5859
};
5960

61+
dictionary TorConfig {
62+
SocketAddress proxy_address;
63+
};
64+
6065
interface NodeEntropy {
6166
[Name=from_bip39_mnemonic]
6267
constructor(Mnemonic mnemonic, string? passphrase);
@@ -126,6 +131,8 @@ interface Builder {
126131
[Throws=BuildError]
127132
void set_announcement_addresses(sequence<SocketAddress> announcement_addresses);
128133
[Throws=BuildError]
134+
void set_tor_config(TorConfig tor_config);
135+
[Throws=BuildError]
129136
void set_node_alias(string node_alias);
130137
[Throws=BuildError]
131138
void set_async_payments_role(AsyncPaymentsRole? role);
@@ -388,6 +395,7 @@ enum BuildError {
388395
"InvalidChannelMonitor",
389396
"InvalidListeningAddresses",
390397
"InvalidAnnouncementAddresses",
398+
"InvalidTorProxyAddress",
391399
"InvalidNodeAlias",
392400
"RuntimeSetupFailed",
393401
"ReadFailed",

src/builder.rs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ use vss_client::headers::VssHeaderProvider;
4545
use crate::chain::ChainSource;
4646
use crate::config::{
4747
default_user_config, may_announce_channel, AnnounceError, AsyncPaymentsRole,
48-
BitcoindRestClientConfig, Config, ElectrumSyncConfig, EsploraSyncConfig,
48+
BitcoindRestClientConfig, Config, ElectrumSyncConfig, EsploraSyncConfig, TorConfig,
4949
DEFAULT_ESPLORA_SERVER_URL, DEFAULT_LOG_FILENAME, DEFAULT_LOG_LEVEL,
5050
};
5151
use crate::connection::ConnectionManager;
@@ -163,6 +163,8 @@ pub enum BuildError {
163163
InvalidListeningAddresses,
164164
/// The given announcement addresses are invalid, e.g. too many were passed.
165165
InvalidAnnouncementAddresses,
166+
/// The given tor proxy address is invalid, e.g. an onion address was passed.
167+
InvalidTorProxyAddress,
166168
/// The provided alias is invalid.
167169
InvalidNodeAlias,
168170
/// An attempt to setup a runtime has failed.
@@ -204,6 +206,7 @@ impl fmt::Display for BuildError {
204206
Self::InvalidAnnouncementAddresses => {
205207
write!(f, "Given announcement addresses are invalid.")
206208
},
209+
Self::InvalidTorProxyAddress => write!(f, "Given Tor proxy address is invalid."),
207210
Self::RuntimeSetupFailed => write!(f, "Failed to setup a runtime."),
208211
Self::ReadFailed => write!(f, "Failed to read from store."),
209212
Self::WriteFailed => write!(f, "Failed to write to store."),
@@ -521,6 +524,23 @@ impl NodeBuilder {
521524
Ok(self)
522525
}
523526

527+
/// Configures the [`Node`] instance to use a Tor SOCKS proxy for some (or all) outbound connections.
528+
/// The proxy address must not itself be an onion address.
529+
///
530+
///
531+
/// **Note**: If unset, connecting to peer OnionV3 addresses will fail.
532+
pub fn set_tor_config(&mut self, tor_config: TorConfig) -> Result<&mut Self, BuildError> {
533+
match tor_config.proxy_address {
534+
SocketAddress::OnionV2 { .. } | SocketAddress::OnionV3 { .. } => {
535+
return Err(BuildError::InvalidTorProxyAddress);
536+
},
537+
_ => {},
538+
}
539+
540+
self.config.tor_config = Some(tor_config);
541+
Ok(self)
542+
}
543+
524544
/// Sets the node alias that will be used when broadcasting announcements to the gossip
525545
/// network.
526546
///
@@ -918,6 +938,14 @@ impl ArcedNodeBuilder {
918938
self.inner.write().unwrap().set_announcement_addresses(announcement_addresses).map(|_| ())
919939
}
920940

941+
/// Configures the [`Node`] instance to use a Tor SOCKS proxy for some (or all) outbound connections.
942+
/// The proxy address must not itself be an onion address.
943+
///
944+
/// **Note**: If unset, connecting to peer OnionV3 addresses will fail.
945+
pub fn set_tor_config(&self, tor_config: TorConfig) -> Result<(), BuildError> {
946+
self.inner.write().unwrap().set_tor_config(tor_config).map(|_| ())
947+
}
948+
921949
/// Sets the node alias that will be used when broadcasting announcements to the gossip
922950
/// network.
923951
///
@@ -1711,8 +1739,12 @@ fn build_with_store_internal(
17111739

17121740
liquidity_source.as_ref().map(|l| l.set_peer_manager(Arc::downgrade(&peer_manager)));
17131741

1714-
let connection_manager =
1715-
Arc::new(ConnectionManager::new(Arc::clone(&peer_manager), Arc::clone(&logger)));
1742+
let connection_manager = Arc::new(ConnectionManager::new(
1743+
Arc::clone(&peer_manager),
1744+
config.tor_config.clone(),
1745+
Arc::clone(&keys_manager),
1746+
Arc::clone(&logger),
1747+
));
17161748

17171749
let output_sweeper = match sweeper_bytes_res {
17181750
Ok(output_sweeper) => Arc::new(output_sweeper),

src/config.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,13 @@ pub struct Config {
192192
/// **Note:** If unset, default parameters will be used, and you will be able to override the
193193
/// parameters on a per-payment basis in the corresponding method calls.
194194
pub route_parameters: Option<RouteParametersConfig>,
195+
/// Configuration options for enabling peer connections via the Tor network.
196+
///
197+
/// Setting [`TorConfig`] enables connecting to Tor-only peers. Please refer to [`TorConfig`]
198+
/// for further information.
199+
///
200+
/// **Note**: If unset, connecting to peer OnionV3 addresses will fail.
201+
pub tor_config: Option<TorConfig>,
195202
}
196203

197204
impl Default for Config {
@@ -204,6 +211,7 @@ impl Default for Config {
204211
trusted_peers_0conf: Vec::new(),
205212
probing_liquidity_limit_multiplier: DEFAULT_PROBING_LIQUIDITY_LIMIT_MULTIPLIER,
206213
anchor_channels_config: Some(AnchorChannelsConfig::default()),
214+
tor_config: None,
207215
route_parameters: None,
208216
node_alias: None,
209217
}
@@ -478,6 +486,15 @@ pub struct BitcoindRestClientConfig {
478486
pub rest_port: u16,
479487
}
480488

489+
/// Configuration for connecting to peers via the Tor Network.
490+
#[derive(Debug, Clone)]
491+
pub struct TorConfig {
492+
/// Tor daemon SOCKS proxy address. Only connections to OnionV3 peers will be made
493+
/// via this proxy; other connections (IPv4 peers, Electrum server) will not be
494+
/// routed over Tor.
495+
pub proxy_address: SocketAddress,
496+
}
497+
481498
/// Options which apply on a per-channel basis and may change at runtime or based on negotiation
482499
/// with our counterparty.
483500
#[derive(Copy, Clone, Debug, PartialEq, Eq)]

src/connection.rs

Lines changed: 116 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ use std::time::Duration;
1414
use bitcoin::secp256k1::PublicKey;
1515
use lightning::ln::msgs::SocketAddress;
1616

17+
use crate::config::TorConfig;
1718
use crate::logger::{log_error, log_info, LdkLogger};
18-
use crate::types::PeerManager;
19+
use crate::types::{KeysManager, PeerManager};
1920
use crate::Error;
2021

2122
pub(crate) struct ConnectionManager<L: Deref + Clone + Sync + Send>
@@ -25,16 +26,22 @@ where
2526
pending_connections:
2627
Mutex<HashMap<PublicKey, Vec<tokio::sync::oneshot::Sender<Result<(), Error>>>>>,
2728
peer_manager: Arc<PeerManager>,
29+
tor_proxy_config: Option<TorConfig>,
30+
keys_manager: Arc<KeysManager>,
2831
logger: L,
2932
}
3033

3134
impl<L: Deref + Clone + Sync + Send> ConnectionManager<L>
3235
where
3336
L::Target: LdkLogger,
3437
{
35-
pub(crate) fn new(peer_manager: Arc<PeerManager>, logger: L) -> Self {
38+
pub(crate) fn new(
39+
peer_manager: Arc<PeerManager>, tor_proxy_config: Option<TorConfig>,
40+
keys_manager: Arc<KeysManager>, logger: L,
41+
) -> Self {
3642
let pending_connections = Mutex::new(HashMap::new());
37-
Self { pending_connections, peer_manager, logger }
43+
44+
Self { pending_connections, peer_manager, tor_proxy_config, keys_manager, logger }
3845
}
3946

4047
pub(crate) async fn connect_peer_if_necessary(
@@ -64,27 +71,114 @@ where
6471

6572
log_info!(self.logger, "Connecting to peer: {}@{}", node_id, addr);
6673

67-
let socket_addr = addr
68-
.to_socket_addrs()
69-
.map_err(|e| {
70-
log_error!(self.logger, "Failed to resolve network address {}: {}", addr, e);
71-
self.propagate_result_to_subscribers(&node_id, Err(Error::InvalidSocketAddress));
72-
Error::InvalidSocketAddress
73-
})?
74-
.next()
75-
.ok_or_else(|| {
76-
log_error!(self.logger, "Failed to resolve network address {}", addr);
74+
let res = match addr {
75+
SocketAddress::OnionV2(old_onion_addr) => {
76+
log_error!(
77+
self.logger,
78+
"Failed to resolve network address {:?}: Resolution of OnionV2 addresses is currently unsupported.",
79+
old_onion_addr
80+
);
7781
self.propagate_result_to_subscribers(&node_id, Err(Error::InvalidSocketAddress));
78-
Error::InvalidSocketAddress
79-
})?;
82+
return Err(Error::InvalidSocketAddress);
83+
},
84+
SocketAddress::OnionV3 { .. } => {
85+
let proxy_config = self.tor_proxy_config.as_ref().ok_or_else(|| {
86+
log_error!(
87+
self.logger,
88+
"Failed to resolve network address {:?}: Tor usage is not configured.",
89+
addr
90+
);
91+
self.propagate_result_to_subscribers(
92+
&node_id,
93+
Err(Error::InvalidSocketAddress),
94+
);
95+
Error::InvalidSocketAddress
96+
})?;
97+
let proxy_addr = proxy_config
98+
.proxy_address
99+
.to_socket_addrs()
100+
.map_err(|e| {
101+
log_error!(
102+
self.logger,
103+
"Failed to resolve Tor proxy network address {}: {}",
104+
proxy_config.proxy_address,
105+
e
106+
);
107+
self.propagate_result_to_subscribers(
108+
&node_id,
109+
Err(Error::InvalidSocketAddress),
110+
);
111+
Error::InvalidSocketAddress
112+
})?
113+
.next()
114+
.ok_or_else(|| {
115+
log_error!(
116+
self.logger,
117+
"Failed to resolve Tor proxy network address {}",
118+
proxy_config.proxy_address
119+
);
120+
self.propagate_result_to_subscribers(
121+
&node_id,
122+
Err(Error::InvalidSocketAddress),
123+
);
124+
Error::InvalidSocketAddress
125+
})?;
126+
let connection_future = lightning_net_tokio::tor_connect_outbound(
127+
Arc::clone(&self.peer_manager),
128+
node_id,
129+
addr.clone(),
130+
proxy_addr,
131+
Arc::clone(&self.keys_manager),
132+
);
133+
self.await_connection(connection_future, node_id, addr).await
134+
},
135+
_ => {
136+
let socket_addr = addr
137+
.to_socket_addrs()
138+
.map_err(|e| {
139+
log_error!(
140+
self.logger,
141+
"Failed to resolve network address {}: {}",
142+
addr,
143+
e
144+
);
145+
self.propagate_result_to_subscribers(
146+
&node_id,
147+
Err(Error::InvalidSocketAddress),
148+
);
149+
Error::InvalidSocketAddress
150+
})?
151+
.next()
152+
.ok_or_else(|| {
153+
log_error!(self.logger, "Failed to resolve network address {}", addr);
154+
self.propagate_result_to_subscribers(
155+
&node_id,
156+
Err(Error::InvalidSocketAddress),
157+
);
158+
Error::InvalidSocketAddress
159+
})?;
160+
let connection_future = lightning_net_tokio::connect_outbound(
161+
Arc::clone(&self.peer_manager),
162+
node_id,
163+
socket_addr,
164+
);
165+
self.await_connection(connection_future, node_id, addr).await
166+
},
167+
};
80168

81-
let connection_future = lightning_net_tokio::connect_outbound(
82-
Arc::clone(&self.peer_manager),
83-
node_id,
84-
socket_addr,
85-
);
169+
self.propagate_result_to_subscribers(&node_id, res);
86170

87-
let res = match connection_future.await {
171+
res
172+
}
173+
174+
async fn await_connection<F, CF>(
175+
&self, connection_future: F, node_id: PublicKey, addr: SocketAddress,
176+
) -> Result<(), Error>
177+
where
178+
F: std::future::Future<Output = Option<CF>>,
179+
CF: std::future::Future<Output = ()>,
180+
{
181+
match connection_future.await {
88182
Some(connection_closed_future) => {
89183
let mut connection_closed_future = Box::pin(connection_closed_future);
90184
loop {
@@ -106,11 +200,7 @@ where
106200
log_error!(self.logger, "Failed to connect to peer: {}@{}", node_id, addr);
107201
Err(Error::ConnectionFailed)
108202
},
109-
};
110-
111-
self.propagate_result_to_subscribers(&node_id, res);
112-
113-
res
203+
}
114204
}
115205

116206
fn register_or_subscribe_pending_connection(

src/ffi/types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub use vss_client::headers::{VssHeaderProvider, VssHeaderProviderError};
4646
use crate::builder::sanitize_alias;
4747
pub use crate::config::{
4848
default_config, AnchorChannelsConfig, BackgroundSyncConfig, ElectrumSyncConfig,
49-
EsploraSyncConfig, MaxDustHTLCExposure, SyncTimeoutsConfig,
49+
EsploraSyncConfig, MaxDustHTLCExposure, SyncTimeoutsConfig, TorConfig,
5050
};
5151
pub use crate::entropy::{generate_entropy_mnemonic, EntropyError, NodeEntropy, WordCount};
5252
use crate::error::Error;

0 commit comments

Comments
 (0)