use std::{
io::{self, Read, Write, stdin, stdout},
path::PathBuf,
};
use anyhow::Context;
use bytes::Bytes;
use clap::Parser;
use geph5_client::{Client, Config};
#[derive(Parser)]
struct CliArgs {
#[arg(short, long)]
config: PathBuf,
#[arg(long)]
stdio_vpn: bool,
#[arg(long)]
#[cfg(unix)]
vpn_fd: Option<i32>,
}
fn main() -> anyhow::Result<()> {
let args = CliArgs::parse();
let config: serde_json::Value = serde_yaml::from_slice(&std::fs::read(args.config)?)?;
let config: Config = serde_json::from_value(config)?;
#[cfg(unix)]
let client = Client::start_with_vpn_fd(config, args.vpn_fd);
#[cfg(not(unix))]
let client = Client::start(config);
if args.stdio_vpn {
run_stdio_vpn(client.clone())?;
}
geph5_rt::block_on(client.wait_until_dead())?;
Ok(())
}
fn run_stdio_vpn(client: Client) -> anyhow::Result<()> {
let client_clone = client.clone();
std::thread::spawn(move || -> anyhow::Result<()> {
let mut stdin = stdin().lock();
let mut length_buf = [0u8; 2];
loop {
match stdin.read_exact(&mut length_buf) {
Ok(_) => {
let length = u16::from_be_bytes(length_buf) as usize;
let mut packet_buf = vec![0u8; length];
stdin.read_exact(&mut packet_buf)?;
geph5_rt::block_on(client_clone.send_vpn_packet(Bytes::from(packet_buf)))?;
}
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
break;
}
Err(e) => {
return Err(e).context("Error reading from stdin");
}
}
}
Ok(())
});
std::thread::spawn(move || -> anyhow::Result<()> {
let mut stdout = stdout().lock();
loop {
let packet = geph5_rt::block_on(client.recv_vpn_packet())?;
let length = std::cmp::min(packet.len(), u16::MAX as usize) as u16;
stdout.write_all(&length.to_be_bytes())?;
stdout.write_all(&packet)?;
stdout.flush()?;
}
});
Ok(())
}