mtunnel 0.1.0

A tcp over http2 proxy
Documentation
use std::fs::File;
use std::io::{self, BufReader};

use tokio::net::{TcpListener, TcpStream};
use tokio_rustls::rustls::ClientConfig;

use mtunnel::args::parse_args;
use mtunnel::config::Config;
use mtunnel::connection::Connection;
use mtunnel::ALPN_HTTP2;

fn tls_config(cfg: &Config) -> io::Result<ClientConfig> {
    let mut config = ClientConfig::new();
    let mut pem = BufReader::new(File::open(&cfg.ca_certificate)?);
    config.root_store.add_pem_file(&mut pem).map_err(|e| {
        log::error!("add pem file err {:?}", e);
        io::Error::new(io::ErrorKind::InvalidInput, "invalid cert")
    })?;

    config.set_protocols(&[ALPN_HTTP2.to_vec()]);
    Ok(config)
}

#[tokio::main]
pub async fn main() -> io::Result<()> {
    env_logger::init();
    let config = parse_args("mtunnel-client").expect("invalid config");
    log::info!("{}", serde_json::to_string_pretty(&config).unwrap());

    let tls_config = tls_config(&config)?;
    let listener = TcpListener::bind(&config.local_addr).await?;
    let remote_addr = config.remote_addr.parse().expect("invalid remote addr");
    let mut h2 = Connection::new(tls_config, remote_addr, config.domain_name).await?;

    loop {
        match listener.accept().await {
            Ok((stream, addr)) => {
                log::debug!("accept tcp stream from {:?}", addr);
                if let Err(e) = proxy(stream, &mut h2).await {
                    log::error!("proxy error {:?}", e);
                }
            }
            Err(e) => {
                log::error!("accept fail: {:?}", e);
            }
        }
    }
}

async fn proxy(socket: TcpStream, h2: &mut Connection) -> io::Result<()> {
    let stream = h2.new_stream().await?;
    tokio::spawn(async move {
        mtunnel::proxy(socket, stream).await;
    });
    Ok(())
}