hotaru_tls 0.8.2

TLS transport for the Hotaru framework
Documentation
//! TLS inbound and outbound runtime objects.

use hotaru_core::connection::{Accepter, Connector, Inbound, Outbound};
use tokio::net::TcpListener;

use super::{TlsAccepter, TlsConnector, TlsStream};
use crate::config::{TlsClientConfig, TlsConfig};

/// Server-side TLS bind target.
///
/// Carries both the TCP bind address and the TLS server config needed to
/// create the bound runtime.
#[derive(Clone)]
pub struct TlsInboundTarget {
    pub addr: String,
    pub config: TlsConfig,
}

impl TlsInboundTarget {
    pub fn new(addr: impl Into<String>, config: TlsConfig) -> Self {
        Self {
            addr: addr.into(),
            config,
        }
    }
}

/// Bound TLS inbound runtime.
pub struct TlsInbound {
    listener: TcpListener,
    accepter: TlsAccepter,
}

impl Inbound for TlsInbound {
    type Wire = TlsStream;
    type BindTarget = TlsInboundTarget;
    type Error = std::io::Error;

    async fn bind(target: Self::BindTarget) -> std::io::Result<Self> {
        let listener = TcpListener::bind(target.addr).await?;
        let accepter = TlsAccepter::new(target.config)
            .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidInput, err))?;

        Ok(Self { listener, accepter })
    }

    async fn accept(&self) -> std::io::Result<Self::Wire> {
        let (tcp, _) = self.listener.accept().await?;
        // `TlsUpgradeError` -> `io::Error` via `From` (see accepter.rs).
        Ok(self.accepter.upgrade(tcp).await?)
    }
}

/// Client-side TLS connect target.
///
/// Carries the remote host/port and TLS client config. The hostname is also
/// used for SNI and certificate verification.
#[derive(Clone)]
pub struct TlsOutboundTarget {
    pub host: String,
    pub port: u16,
    pub config: TlsClientConfig,
}

impl TlsOutboundTarget {
    pub fn new(host: impl Into<String>, port: u16, config: TlsClientConfig) -> Self {
        Self {
            host: host.into(),
            port,
            config,
        }
    }
}

/// TLS outbound runtime bound to one remote target.
pub struct TlsOutbound {
    target: (String, u16),
    connector: TlsConnector,
}

impl TlsOutbound {
    pub fn target(&self) -> (&str, u16) {
        (&self.target.0, self.target.1)
    }
}

impl Outbound for TlsOutbound {
    type Wire = TlsStream;
    type ConnectTarget = TlsOutboundTarget;
    type Error = std::io::Error;

    async fn build(target: Self::ConnectTarget) -> std::io::Result<Self> {
        let connector = TlsConnector::new(target.config)
            .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidInput, err))?;

        Ok(Self {
            target: (target.host, target.port),
            connector,
        })
    }

    async fn connect(&self) -> std::io::Result<Self::Wire> {
        // `TlsConnectError` -> `io::Error` via `From` (see connector.rs).
        Ok(self.connector.connect(self.target.clone()).await?)
    }
}