diff --git a/xmr-btc/src/tor.rs b/xmr-btc/src/tor.rs index fbc9f191..c5a6d939 100644 --- a/xmr-btc/src/tor.rs +++ b/xmr-btc/src/tor.rs @@ -10,53 +10,78 @@ use torut::{ onion::TorSecretKeyV3, }; -lazy_static! { - /// The default TOR socks5 proxy address, `127.0.0.1:9050`. - pub static ref TOR_PROXY_ADDR: SocketAddrV4 = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 9050); - /// The default TOR Controller Protocol address, `127.0.0.1:9051`. - pub static ref TOR_CP_ADDR: SocketAddr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 9051)); -} - -/// checks if tor is running -async fn tor_running() -> Result<()> { - // Make sure you are running tor and this is your socks port - let proxy = reqwest::Proxy::all(format!("socks5h://{}", *TOR_PROXY_ADDR).as_str()) - .expect("tor proxy should be there"); - let client = reqwest::Client::builder() - .proxy(proxy) - .build() - .expect("should be able to build reqwest client"); - - let res = client.get("https://check.torproject.org").send().await?; - - let text = res.text().await?; - let is_tor = text.contains("Congratulations. This browser is configured to use Tor."); - - if is_tor { - Ok(()) - } else { - bail!("Tor is currently not running") - } -} +// lazy_static! { +// The default TOR socks5 proxy address, `127.0.0.1:9050`. +// pub static ref TOR_PROXY_ADDR: SocketAddrV4 = +// SocketAddrV4::new(Ipv4Addr::LOCALHOST, 9050); The default TOR Controller +// Protocol address, `127.0.0.1:9051`. pub static ref TOR_CP_ADDR: SocketAddr = +// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 9051)); } type Handler = fn(AsyncEvent<'_>) -> Box> + Unpin>; #[allow(missing_debug_implementations)] -pub struct AuthenticatedConnection(AuthenticatedConn); +pub struct AuthenticatedConnection { + tor_proxy_address: SocketAddrV4, + tor_control_port_address: SocketAddr, + authenticated_connection: Option>, +} + +impl Default for AuthenticatedConnection { + fn default() -> Self { + Self { + tor_proxy_address: SocketAddrV4::new(Ipv4Addr::LOCALHOST, 9050), + tor_control_port_address: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 9051)), + authenticated_connection: None, + } + } +} impl AuthenticatedConnection { - async fn init_unauthenticated_connection() -> Result> { + /// checks if tor is running + async fn tor_running(&self) -> Result<()> { + // Make sure you are running tor and this is your socks port + let proxy = reqwest::Proxy::all(format!("socks5h://{}", self.tor_proxy_address).as_str()) + .expect("tor proxy should be there"); + let client = reqwest::Client::builder() + .proxy(proxy) + .build() + .expect("should be able to build reqwest client"); + + let res = client.get("https://check.torproject.org").send().await?; + + let text = res.text().await?; + let is_tor = text.contains("Congratulations. This browser is configured to use Tor."); + + if is_tor { + Ok(()) + } else { + bail!("Tor is currently not running") + } + } + + async fn init_unauthenticated_connection(&self) -> Result> { // try to connect to local tor service via control port - let sock = TcpStream::connect(*TOR_CP_ADDR).await?; + let sock = TcpStream::connect(self.tor_control_port_address).await?; let unauthenticated_connection = UnauthenticatedConn::new(sock); Ok(unauthenticated_connection) } - /// Create a new authenticated connection to your local Tor service - pub async fn new() -> Result { - tor_running().await?; + pub fn with_ports(proxy_port: u16, control_port: u16) -> Self { + Self { + tor_proxy_address: SocketAddrV4::new(Ipv4Addr::LOCALHOST, proxy_port), + tor_control_port_address: SocketAddr::V4(SocketAddrV4::new( + Ipv4Addr::LOCALHOST, + control_port, + )), + authenticated_connection: None, + } + } - let mut unauthenticated_connection = match Self::init_unauthenticated_connection().await { + /// Create a new authenticated connection to your local Tor service + pub async fn connect(self) -> Result { + self.tor_running().await?; + + let mut unauthenticated_connection = match self.init_unauthenticated_connection().await { Err(_) => bail!("Tor instance not running"), Ok(unauthenticated_connection) => unauthenticated_connection, }; @@ -79,26 +104,34 @@ impl AuthenticatedConnection { } let authenticated_connection = unauthenticated_connection.into_authenticated().await; - Ok(AuthenticatedConnection(authenticated_connection)) + Ok(AuthenticatedConnection { + authenticated_connection: Some(authenticated_connection), + ..self + }) } /// Add an ephemeral tor service on localhost with the provided key pub async fn add_service(&mut self, port: u16, tor_key: &TorSecretKeyV3) -> Result<()> { - self.0 - .add_onion_v3( - tor_key, - false, - false, - false, - None, - &mut [( - port, - SocketAddr::new(IpAddr::from(Ipv4Addr::new(127, 0, 0, 1)), port), - )] - .iter(), - ) - .await - .map_err(|_| anyhow!("Could not add onion service."))?; + match self.authenticated_connection { + None => bail!("Not connected to local tor instance"), + Some(ref mut aut) => { + aut.add_onion_v3( + tor_key, + false, + false, + false, + None, + &mut [( + port, + SocketAddr::new(IpAddr::from(Ipv4Addr::new(127, 0, 0, 1)), port), + )] + .iter(), + ) + .await + .map_err(|_| anyhow!("Could not add onion service."))?; + } + } + Ok(()) } } diff --git a/xmr-btc/tests/tor.rs b/xmr-btc/tests/tor.rs index 6deac6b3..f9243357 100644 --- a/xmr-btc/tests/tor.rs +++ b/xmr-btc/tests/tor.rs @@ -5,10 +5,13 @@ mod tor_test { use hyper::service::{make_service_fn, service_fn}; use reqwest::StatusCode; use spectral::prelude::*; - use std::convert::Infallible; + use std::{convert::Infallible, process::Child}; use tokio::sync::oneshot::Receiver; - use torut::onion::TorSecretKeyV3; - use xmr_btc::tor::{AuthenticatedConnection, TOR_PROXY_ADDR}; + use torut::{ + onion::TorSecretKeyV3, + utils::{run_tor, AutoKillChild}, + }; + use xmr_btc::tor::AuthenticatedConnection; async fn hello_world( _req: hyper::Request, @@ -32,15 +35,43 @@ mod tor_test { }); } + fn run_tmp_tor() -> (Child, u16, u16) { + let control_port = 9051; + let proxy_port = 9050; + + ( + run_tor( + "tor", + &mut [ + "--CookieAuthentication", + "1", + "--ControlPort", + control_port.to_string().as_str(), + ] + .iter(), + ) + .expect("Starting tor filed"), + control_port, + proxy_port, + ) + } + #[tokio::test] async fn test_tor_control_port() -> Result<()> { + // start tmp tor + let (child, control_port, proxy_port) = run_tmp_tor(); + let _child = AutoKillChild::new(child); + // Setup test HTTP Server let (tx, rx) = tokio::sync::oneshot::channel::<()>(); let port = 8080; start_test_service(port, rx); // Connect to local Tor service - let mut authenticated_connection = AuthenticatedConnection::new().await?; + let mut authenticated_connection = + AuthenticatedConnection::with_ports(proxy_port, control_port) + .connect() + .await?; // Expose an onion service that re-directs to the echo server. let tor_secret_key_v3 = TorSecretKeyV3::generate(); @@ -50,7 +81,7 @@ mod tor_test { // Test if Tor service forwards to HTTP Server - let proxy = reqwest::Proxy::all(format!("socks5h://{}", *TOR_PROXY_ADDR).as_str()) + let proxy = reqwest::Proxy::all(format!("socks5h://127.0.0.1:{}", proxy_port).as_str()) .expect("tor proxy should be there"); let client = reqwest::Client::builder().proxy(proxy).build().unwrap(); let onion_address = tor_secret_key_v3.public().get_onion_address().to_string();