tunneler 0.4.0

Tunnel TCP or UDP traffic over TCP, (mutual) TLS or DNS (authoritative server or direct connection)
Documentation
use std::error::Error;
use std::net::IpAddr;
use std::path::PathBuf;
use std::sync::Arc;

use async_trait::async_trait;
use tokio::io::split;
use tokio::net::TcpStream;
use tokio_rustls::rustls::ClientConfig;
use tokio_rustls::webpki::DNSNameRef;
use tokio_rustls::TlsConnector;

use common::io::Stream;
use common::tls::{load_certificate, load_private_key};

use crate::tunnel::{TcpTunneler, Tunneler};

pub(crate) struct TlsTunneler {
    tunneler: TcpTunneler,
}

impl TlsTunneler {
    pub(crate) async fn new(
        address: IpAddr,
        port: u16,
        ca_cert: PathBuf,
        client_cert: PathBuf,
        client_key: PathBuf,
        server_hostname: String,
    ) -> Result<Self, Box<dyn Error>> {
        let mut config = ClientConfig::new();
        config.root_store.add(&load_certificate(ca_cert).await?)?;
        let chain = vec![load_certificate(client_cert).await?];
        config.set_single_client_cert(chain, load_private_key(client_key).await?)?;
        let connector = TlsConnector::from(Arc::new(config));

        let to_address = format!("{}:{}", address, port);
        log::debug!("connecting to {}", to_address);
        let tcp_stream = TcpStream::connect(to_address).await?;

        let domain = DNSNameRef::try_from_ascii_str(&server_hostname)?;
        let stream = connector.connect(domain, tcp_stream).await?;
        let (reader, writer) = split(stream);
        Ok(Self {
            tunneler: TcpTunneler::from_stream(Stream::new(reader, writer))?,
        })
    }
}

#[async_trait]
impl Tunneler for TlsTunneler {
    async fn tunnel(&mut self, client: Stream) -> Result<(), Box<dyn Error>> {
        self.tunneler.tunnel(client).await
    }
}