ocapn-netlayer 0.1.4

OCapN transport layer interfaces and types
Documentation
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()))
            }
        }
    }
}