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::sync::Arc;
use std::time::Duration;

use async_channel::Receiver;
use simple_logger::SimpleLogger;
use structopt::StructOpt;

use common::cli::{Cli, TunneledType, TunnelerType};
use common::io::Stream;
use common::network::{Listener, TcpListener};

use crate::dns::DnsTunneler;
use crate::network::UdpListener;
use crate::tls::TlsTunneler;
use crate::tunnel::{TcpTunneler, Tunneler};

mod dns;
mod network;
mod tls;
mod tunnel;

fn main() -> Result<(), Box<dyn Error>> {
    let args: Cli = Cli::from_args();

    SimpleLogger::new()
        .with_level(args.log_level)
        .init()
        .unwrap();
    log::debug!("start client - args are {:?}", args);

    start_client(args)
}

#[tokio::main]
async fn start_client(args: Cli) -> Result<(), Box<dyn Error>> {
    let mut listener =
        new_listener(args.tunneled_type, args.local_address, args.local_port).await?;
    let (clients_sender, clients_receiver) = async_channel::unbounded::<Stream>();

    let tunnel_type = Arc::new(args.tunneler_type);
    let accept_clients_future = listener.accept_clients(clients_sender);
    let tunnel_clients_future = tunnel_clients(
        clients_receiver,
        tunnel_type.clone(),
        args.remote_address,
        args.remote_port,
    );
    match tokio::try_join!(accept_clients_future, tunnel_clients_future) {
        Ok(_) => Ok(()),
        Err(e) => Err(e),
    }
}

async fn new_listener(
    type_: TunneledType,
    address: IpAddr,
    port: u16,
) -> Result<Box<dyn Listener>, Box<dyn Error>> {
    match type_ {
        TunneledType::Tcp => Ok(Box::new(TcpListener::new(address, port).await?)),
        TunneledType::Udp => Ok(Box::new(UdpListener::new(address, port).await?)),
    }
}

async fn new_tunneler(
    type_: &TunnelerType,
    address: IpAddr,
    port: u16,
) -> Result<Box<dyn Tunneler>, Box<dyn Error>> {
    match type_ {
        TunnelerType::Tcp => Ok(Box::new(TcpTunneler::new(address, port).await?)),
        TunnelerType::Dns {
            read_timeout_in_milliseconds,
            idle_client_timeout_in_milliseconds,
            client_suffix,
        } => Ok(Box::new(
            DnsTunneler::new(
                address,
                port,
                Duration::from_millis(*read_timeout_in_milliseconds),
                Duration::from_millis(*idle_client_timeout_in_milliseconds),
                client_suffix.clone(),
            )
            .await?,
        )),
        TunnelerType::Tls {
            ca_cert,
            cert,
            key,
            server_hostname,
        } => Ok(Box::new(
            TlsTunneler::new(
                address,
                port,
                ca_cert.to_path_buf(),
                cert.to_path_buf(),
                key.to_path_buf(),
                server_hostname.clone(),
            )
            .await?,
        )),
    }
}

async fn tunnel_clients(
    clients: Receiver<Stream>,
    tunnel_type: Arc<TunnelerType>,
    remote_address: IpAddr,
    remote_port: u16,
) -> Result<(), Box<dyn Error>> {
    while let Ok(client) = clients.recv().await {
        tokio::spawn(tunnel_client(
            client,
            tunnel_type.clone(),
            remote_address,
            remote_port,
        ));
    }
    Ok(())
}

async fn tunnel_client(
    client: Stream,
    tunnel_type: Arc<TunnelerType>,
    remote_address: IpAddr,
    remote_port: u16,
) {
    let mut tunnel = match new_tunneler(&tunnel_type, remote_address, remote_port).await {
        Ok(t) => t,
        Err(e) => {
            log::error!(
                "failed to create {:?} tunnel to {}:{}: {}",
                tunnel_type,
                remote_address,
                remote_port,
                e
            );
            return;
        }
    };
    if let Err(e) = tunnel.tunnel(client).await {
        log::error!("failed to tunnel client: {}", e);
    }
}