Skip to content

Commit 11a9d8a

Browse files
committed
Add Tor support for outbound connections via SOCKS
1 parent a8b956a commit 11a9d8a

File tree

5 files changed

+184
-30
lines changed

5 files changed

+184
-30
lines changed

bindings/ldk_node.udl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ typedef dictionary EsploraSyncConfig;
99

1010
typedef dictionary ElectrumSyncConfig;
1111

12+
typedef dictionary TorConfig;
13+
1214
typedef interface NodeEntropy;
1315

1416
typedef enum WordCount;
@@ -53,6 +55,8 @@ interface Builder {
5355
[Throws=BuildError]
5456
void set_announcement_addresses(sequence<SocketAddress> announcement_addresses);
5557
[Throws=BuildError]
58+
void set_tor_config(TorConfig tor_config);
59+
[Throws=BuildError]
5660
void set_node_alias(string node_alias);
5761
[Throws=BuildError]
5862
void set_async_payments_role(AsyncPaymentsRole? role);

src/builder.rs

Lines changed: 45 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;
@@ -165,6 +165,8 @@ pub enum BuildError {
165165
InvalidListeningAddresses,
166166
/// The given announcement addresses are invalid, e.g. too many were passed.
167167
InvalidAnnouncementAddresses,
168+
/// The given tor proxy address is invalid, e.g. an onion address was passed.
169+
InvalidTorProxyAddress,
168170
/// The provided alias is invalid.
169171
InvalidNodeAlias,
170172
/// An attempt to setup a runtime has failed.
@@ -206,6 +208,7 @@ impl fmt::Display for BuildError {
206208
Self::InvalidAnnouncementAddresses => {
207209
write!(f, "Given announcement addresses are invalid.")
208210
},
211+
Self::InvalidTorProxyAddress => write!(f, "Given Tor proxy address is invalid."),
209212
Self::RuntimeSetupFailed => write!(f, "Failed to setup a runtime."),
210213
Self::ReadFailed => write!(f, "Failed to read from store."),
211214
Self::WriteFailed => write!(f, "Failed to write to store."),
@@ -523,6 +526,23 @@ impl NodeBuilder {
523526
Ok(self)
524527
}
525528

529+
/// Configures the [`Node`] instance to use a Tor SOCKS proxy for outbound connections to peers with OnionV3 addresses.
530+
/// Connections to clearnet addresses are not affected, and are not made over Tor.
531+
/// The proxy address must not itself be an onion address.
532+
///
533+
/// **Note**: If unset, connecting to peer OnionV3 addresses will fail.
534+
pub fn set_tor_config(&mut self, tor_config: TorConfig) -> Result<&mut Self, BuildError> {
535+
match tor_config.proxy_address {
536+
SocketAddress::OnionV2 { .. } | SocketAddress::OnionV3 { .. } => {
537+
return Err(BuildError::InvalidTorProxyAddress);
538+
},
539+
_ => {},
540+
}
541+
542+
self.config.tor_config = Some(tor_config);
543+
Ok(self)
544+
}
545+
526546
/// Sets the node alias that will be used when broadcasting announcements to the gossip
527547
/// network.
528548
///
@@ -920,6 +940,15 @@ impl ArcedNodeBuilder {
920940
self.inner.write().unwrap().set_announcement_addresses(announcement_addresses).map(|_| ())
921941
}
922942

943+
/// Configures the [`Node`] instance to use a Tor SOCKS proxy for outbound connections to peers with OnionV3 addresses.
944+
/// Connections to clearnet addresses are not affected, and are not made over Tor.
945+
/// The proxy address must not itself be an onion address.
946+
///
947+
/// **Note**: If unset, connecting to peer OnionV3 addresses will fail.
948+
pub fn set_tor_config(&self, tor_config: TorConfig) -> Result<(), BuildError> {
949+
self.inner.write().unwrap().set_tor_config(tor_config).map(|_| ())
950+
}
951+
923952
/// Sets the node alias that will be used when broadcasting announcements to the gossip
924953
/// network.
925954
///
@@ -1076,6 +1105,15 @@ fn build_with_store_internal(
10761105
}
10771106
}
10781107

1108+
if let Some(tor_config) = &config.tor_config {
1109+
match tor_config.proxy_address {
1110+
SocketAddress::OnionV2 { .. } | SocketAddress::OnionV3 { .. } => {
1111+
return Err(BuildError::InvalidTorProxyAddress);
1112+
},
1113+
_ => {},
1114+
}
1115+
}
1116+
10791117
let tx_broadcaster = Arc::new(TransactionBroadcaster::new(Arc::clone(&logger)));
10801118
let fee_estimator = Arc::new(OnchainFeeEstimator::new());
10811119

@@ -1709,8 +1747,12 @@ fn build_with_store_internal(
17091747

17101748
liquidity_source.as_ref().map(|l| l.set_peer_manager(Arc::downgrade(&peer_manager)));
17111749

1712-
let connection_manager =
1713-
Arc::new(ConnectionManager::new(Arc::clone(&peer_manager), Arc::clone(&logger)));
1750+
let connection_manager = Arc::new(ConnectionManager::new(
1751+
Arc::clone(&peer_manager),
1752+
config.tor_config.clone(),
1753+
Arc::clone(&keys_manager),
1754+
Arc::clone(&logger),
1755+
));
17141756

17151757
let output_sweeper = match sweeper_bytes_res {
17161758
Ok(output_sweeper) => Arc::new(output_sweeper),

src/config.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,13 @@ pub struct Config {
196196
/// **Note:** If unset, default parameters will be used, and you will be able to override the
197197
/// parameters on a per-payment basis in the corresponding method calls.
198198
pub route_parameters: Option<RouteParametersConfig>,
199+
/// Configuration options for enabling peer connections via the Tor network.
200+
///
201+
/// Setting [`TorConfig`] enables connecting to peers with OnionV3 addresses. No other connections
202+
/// are routed via Tor. Please refer to [`TorConfig`] for further information.
203+
///
204+
/// **Note**: If unset, connecting to peer OnionV3 addresses will fail.
205+
pub tor_config: Option<TorConfig>,
199206
}
200207

201208
impl Default for Config {
@@ -208,6 +215,7 @@ impl Default for Config {
208215
trusted_peers_0conf: Vec::new(),
209216
probing_liquidity_limit_multiplier: DEFAULT_PROBING_LIQUIDITY_LIMIT_MULTIPLIER,
210217
anchor_channels_config: Some(AnchorChannelsConfig::default()),
218+
tor_config: None,
211219
route_parameters: None,
212220
node_alias: None,
213221
}
@@ -487,6 +495,16 @@ pub struct BitcoindRestClientConfig {
487495
pub rest_port: u16,
488496
}
489497

498+
/// Configuration for connecting to peers via the Tor Network.
499+
#[derive(Debug, Clone)]
500+
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
501+
pub struct TorConfig {
502+
/// Tor daemon SOCKS proxy address. Only connections to OnionV3 peers will be made
503+
/// via this proxy; other connections (IPv4 peers, Electrum server) will not be
504+
/// routed over Tor.
505+
pub proxy_address: SocketAddress,
506+
}
507+
490508
/// Options which apply on a per-channel basis and may change at runtime or based on negotiation
491509
/// with our counterparty.
492510
#[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
@@ -138,7 +138,7 @@ impl VssClientHeaderProvider for VssHeaderProviderAdapter {
138138
}
139139

140140
use crate::builder::sanitize_alias;
141-
pub use crate::config::{default_config, ElectrumSyncConfig, EsploraSyncConfig};
141+
pub use crate::config::{default_config, ElectrumSyncConfig, EsploraSyncConfig, TorConfig};
142142
pub use crate::entropy::{generate_entropy_mnemonic, NodeEntropy, WordCount};
143143
use crate::error::Error;
144144
pub use crate::liquidity::LSPS1OrderStatus;

0 commit comments

Comments
 (0)