use std::{env::temp_dir, path::PathBuf, str::FromStr};
use anyhow::Error;
use async_trait::async_trait;
use tokio::net::TcpStream;
use tor_interface::{
legacy_tor_client::{LegacyTorClient, LegacyTorClientConfig},
tor_crypto::{Ed25519PrivateKey, V3OnionServiceId},
tor_provider::{OnionAddr, OnionAddrV3, OnionListener, TargetAddr, TorProvider},
};
use crate::{connector::Connector, Result};
pub struct Config {
tor_bin_path: PathBuf,
data_directory: PathBuf,
service_key: Ed25519PrivateKey,
}
impl Default for Config {
fn default() -> Self {
let mut data_path = temp_dir();
data_path.push("ocapn-netlayer:tor");
Config {
tor_bin_path: PathBuf::from_str("/usr/bin/tor").unwrap(),
data_directory: data_path,
service_key: Ed25519PrivateKey::generate(),
}
}
}
pub(crate) struct TorConnector {
tor_client: Box<dyn TorProvider>,
listener: OnionListener,
}
const TOR_CONNECTOR_PORT: u16 = 9045;
impl TryFrom<Config> for TorConnector {
type Error = Error;
fn try_from(config: Config) -> Result<Self> {
let mut tor_client = LegacyTorClient::new(LegacyTorClientConfig::BundledTor {
tor_bin_path: config.tor_bin_path,
data_directory: config.data_directory,
proxy_settings: None,
allowed_ports: None,
pluggable_transports: None,
bridge_lines: None,
})?;
let listener = tor_client.listener(&config.service_key, TOR_CONNECTOR_PORT, None)?;
Ok(TorConnector {
tor_client: Box::new(tor_client),
listener,
})
}
}
#[async_trait]
impl Connector for TorConnector {
async fn new_outgoing_connection(
&mut self,
locator: &crate::locator::Peer,
) -> Result<crate::connection::Connection> {
if !locator.transport.eq("onion") {
return Err(Error::msg("not an onion address"));
}
let std_stream = self.tor_client.connect(
TargetAddr::OnionService(OnionAddr::V3(OnionAddrV3::new(
V3OnionServiceId::from_string(&locator.designator)?,
TOR_CONNECTOR_PORT,
))),
None,
)?;
let stream = TcpStream::from_std(std_stream.into())?;
Ok(stream.into())
}
async fn accept_incoming_connection(
&mut self,
) -> Result<Option<crate::connection::Connection>> {
match self.listener.accept()? {
None => Ok(None),
Some(std_stream) => {
let stream = TcpStream::from_std(std_stream.into())?;
Ok(Some(stream.into()))
}
}
}
}