-
Notifications
You must be signed in to change notification settings - Fork 122
Add Tor support for outbound connections via SOCKS #778
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -518,6 +518,16 @@ impl NodeBuilder { | |
| Ok(self) | ||
| } | ||
|
|
||
| /// Set the address which [`Node`] will use as a Tor proxy to connect to peer OnionV3 addresses. | ||
| /// | ||
| /// **Note**: If unset, connecting to peer OnionV3 addresses will fail. | ||
| /// | ||
| /// [`tor_proxy_address`]: Config::tor_proxy_address | ||
| pub fn set_tor_proxy_address(&mut self, tor_proxy_address: core::net::SocketAddr) -> &mut Self { | ||
| self.config.tor_proxy_address = Some(tor_proxy_address); | ||
| self | ||
| } | ||
|
|
||
| /// Sets the node alias that will be used when broadcasting announcements to the gossip | ||
| /// network. | ||
| /// | ||
|
|
@@ -904,6 +914,15 @@ impl ArcedNodeBuilder { | |
| self.inner.write().unwrap().set_announcement_addresses(announcement_addresses).map(|_| ()) | ||
| } | ||
|
|
||
| /// Set the address which [`Node`] will use as a Tor proxy to connect to peer OnionV3 addresses. | ||
| /// | ||
| /// **Note**: If unset, connecting to peer OnionV3 addresses will fail. | ||
| /// | ||
| /// [`tor_proxy_address`]: Config::tor_proxy_address | ||
| pub fn set_tor_proxy_address(&mut self, tor_proxy_address: core::net::SocketAddr) { | ||
| self.config.tor_proxy_address = Some(tor_proxy_address); | ||
| } | ||
|
|
||
| /// Sets the node alias that will be used when broadcasting announcements to the gossip | ||
| /// network. | ||
| /// | ||
|
|
@@ -1683,8 +1702,12 @@ fn build_with_store_internal( | |
|
|
||
| liquidity_source.as_ref().map(|l| l.set_peer_manager(Arc::downgrade(&peer_manager))); | ||
|
|
||
| let connection_manager = | ||
| Arc::new(ConnectionManager::new(Arc::clone(&peer_manager), Arc::clone(&logger))); | ||
| let connection_manager = Arc::new(ConnectionManager::new( | ||
| Arc::clone(&peer_manager), | ||
| config.tor_proxy_address.clone(), | ||
| ephemeral_bytes, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will be used to derive passwords sent in plaintext to the SOCKS5 proxy, so out of caution I would have passed a seed here that is different from the seed passed to the The |
||
| Arc::clone(&logger), | ||
| )); | ||
|
|
||
| let output_sweeper = match sweeper_bytes_res { | ||
| Ok(output_sweeper) => Arc::new(output_sweeper), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -149,6 +149,12 @@ pub struct Config { | |
| /// | ||
| /// [`listening_addresses`]: Config::listening_addresses | ||
| pub announcement_addresses: Option<Vec<SocketAddress>>, | ||
| /// The address which the node will use as a Tor proxy to connect to peer OnionV3 addresses. | ||
| /// | ||
| /// **Note**: If unset, connecting to peer OnionV3 addresses will fail. | ||
| /// | ||
| /// [`tor_proxy_address`]: Config::tor_proxy_address | ||
| pub tor_proxy_address: Option<core::net::SocketAddr>, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I think it would be preferable to drop this field and just add a separate Also note that we'll want to expose all of the new APIs to bindings, so you'll need to make the corresponding changes in |
||
| /// The node alias that will be used when broadcasting announcements to the gossip network. | ||
| /// | ||
| /// The provided alias must be a valid UTF-8 string and no longer than 32 bytes in total. | ||
|
|
@@ -201,6 +207,7 @@ impl Default for Config { | |
| network: DEFAULT_NETWORK, | ||
| listening_addresses: None, | ||
| announcement_addresses: None, | ||
| tor_proxy_address: None, | ||
| trusted_peers_0conf: Vec::new(), | ||
| probing_liquidity_limit_multiplier: DEFAULT_PROBING_LIQUIDITY_LIMIT_MULTIPLIER, | ||
| anchor_channels_config: Some(AnchorChannelsConfig::default()), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ use std::time::Duration; | |
|
|
||
| use bitcoin::secp256k1::PublicKey; | ||
| use lightning::ln::msgs::SocketAddress; | ||
| use lightning::sign::RandomBytes; | ||
|
|
||
| use crate::logger::{log_error, log_info, LdkLogger}; | ||
| use crate::types::PeerManager; | ||
|
|
@@ -25,16 +26,23 @@ where | |
| pending_connections: | ||
| Mutex<HashMap<PublicKey, Vec<tokio::sync::oneshot::Sender<Result<(), Error>>>>>, | ||
| peer_manager: Arc<PeerManager>, | ||
| tor_proxy_addr: Option<core::net::SocketAddr>, | ||
| tor_proxy_rng: Arc<RandomBytes>, | ||
| logger: L, | ||
| } | ||
|
|
||
| impl<L: Deref + Clone + Sync + Send> ConnectionManager<L> | ||
| where | ||
| L::Target: LdkLogger, | ||
| { | ||
| pub(crate) fn new(peer_manager: Arc<PeerManager>, logger: L) -> Self { | ||
| pub(crate) fn new( | ||
| peer_manager: Arc<PeerManager>, tor_proxy_addr: Option<core::net::SocketAddr>, | ||
| ephemeral_random_data: [u8; 32], logger: L, | ||
| ) -> Self { | ||
| let pending_connections = Mutex::new(HashMap::new()); | ||
| Self { pending_connections, peer_manager, logger } | ||
| let tor_proxy_rng = Arc::new(RandomBytes::new(ephemeral_random_data)); | ||
|
|
||
| Self { pending_connections, peer_manager, tor_proxy_addr, tor_proxy_rng, logger } | ||
| } | ||
|
|
||
| pub(crate) async fn connect_peer_if_necessary( | ||
|
|
@@ -64,27 +72,73 @@ where | |
|
|
||
| log_info!(self.logger, "Connecting to peer: {}@{}", node_id, addr); | ||
|
|
||
| let socket_addr = addr | ||
| .to_socket_addrs() | ||
| .map_err(|e| { | ||
| log_error!(self.logger, "Failed to resolve network address {}: {}", addr, e); | ||
| self.propagate_result_to_subscribers(&node_id, Err(Error::InvalidSocketAddress)); | ||
| Error::InvalidSocketAddress | ||
| })? | ||
| .next() | ||
| .ok_or_else(|| { | ||
| log_error!(self.logger, "Failed to resolve network address {}", addr); | ||
| let res = if let SocketAddress::OnionV2(old_onion_addr) = addr { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: did you consider using a |
||
| log_error!( | ||
| self.logger, | ||
| "Failed to resolve network address {:?}: Resolution of OnionV2 addresses is currently unsupported.", | ||
| old_onion_addr | ||
| ); | ||
| self.propagate_result_to_subscribers(&node_id, Err(Error::InvalidSocketAddress)); | ||
| return Err(Error::InvalidSocketAddress); | ||
| } else if let SocketAddress::OnionV3 { .. } = addr { | ||
| let proxy_addr = self.tor_proxy_addr.ok_or_else(|| { | ||
| log_error!( | ||
| self.logger, | ||
| "Failed to resolve network address {:?}: Tor proxy address is unset.", | ||
| addr | ||
| ); | ||
| self.propagate_result_to_subscribers(&node_id, Err(Error::InvalidSocketAddress)); | ||
| Error::InvalidSocketAddress | ||
| })?; | ||
| let connection_future = lightning_net_tokio::tor_connect_outbound( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to allow people to proxy the other types over tor as well ? Have to be careful with adding more settings, but I'm thinking of an analog to the Could be done in a follow-up if preferred.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, that would be a useful setting... I'm open to add that on to this PR, would need another cfg setting I suppose.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Let's just add this as a flag in above mentioned |
||
| Arc::clone(&self.peer_manager), | ||
| node_id, | ||
| addr.clone(), | ||
| proxy_addr, | ||
| self.tor_proxy_rng.clone(), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Would have used |
||
| ); | ||
| self.await_connection(connection_future, node_id, addr).await | ||
| } else { | ||
| let socket_addr = addr | ||
| .to_socket_addrs() | ||
| .map_err(|e| { | ||
| log_error!(self.logger, "Failed to resolve network address {}: {}", addr, e); | ||
| self.propagate_result_to_subscribers( | ||
| &node_id, | ||
| Err(Error::InvalidSocketAddress), | ||
| ); | ||
| Error::InvalidSocketAddress | ||
| })? | ||
| .next() | ||
| .ok_or_else(|| { | ||
| log_error!(self.logger, "Failed to resolve network address {}", addr); | ||
| self.propagate_result_to_subscribers( | ||
| &node_id, | ||
| Err(Error::InvalidSocketAddress), | ||
| ); | ||
| Error::InvalidSocketAddress | ||
| })?; | ||
| let connection_future = lightning_net_tokio::connect_outbound( | ||
| Arc::clone(&self.peer_manager), | ||
| node_id, | ||
| socket_addr, | ||
| ); | ||
| self.await_connection(connection_future, node_id, addr).await | ||
| }; | ||
|
|
||
| self.propagate_result_to_subscribers(&node_id, res); | ||
|
|
||
| let connection_future = lightning_net_tokio::connect_outbound( | ||
| Arc::clone(&self.peer_manager), | ||
| node_id, | ||
| socket_addr, | ||
| ); | ||
| res | ||
| } | ||
|
|
||
| let res = match connection_future.await { | ||
| async fn await_connection<F, CF>( | ||
| &self, connection_future: F, node_id: PublicKey, addr: SocketAddress, | ||
| ) -> Result<(), Error> | ||
| where | ||
| F: std::future::Future<Output = Option<CF>>, | ||
| CF: std::future::Future<Output = ()>, | ||
| { | ||
| match connection_future.await { | ||
| Some(connection_closed_future) => { | ||
| let mut connection_closed_future = Box::pin(connection_closed_future); | ||
| loop { | ||
|
|
@@ -106,11 +160,7 @@ where | |
| log_error!(self.logger, "Failed to connect to peer: {}@{}", node_id, addr); | ||
| Err(Error::ConnectionFailed) | ||
| }, | ||
| }; | ||
|
|
||
| self.propagate_result_to_subscribers(&node_id, res); | ||
|
|
||
| res | ||
| } | ||
| } | ||
|
|
||
| fn register_or_subscribe_pending_connection( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than replicating the functionality, please just call through to
inneras we do elsewhere in this builder. This avoids their behavior getting out-of-sync over time.