use core::net::SocketAddr;
use std::{
future::pending,
time::{Duration, Instant},
};
use base64::Engine as _;
use bytes::BufMut;
use clap::Parser as _;
use tokio::{
select,
time::{interval_at, sleep_until},
};
use ts_keys::{NodePrivateKey, NodePublicKey};
use ts_packet::PacketMut;
use ts_time::TimeRange;
use ts_tunnel::Endpoint;
use zerocopy::{FromBytes, IntoBytes, TryFromBytes};
#[derive(clap::Parser, Debug)]
pub struct Args {
#[clap(short, long)]
pub endpoint: SocketAddr,
#[clap(long, value_parser = parse_key)]
pub peer_key: chacha20poly1305::Key,
#[clap(long, value_parser = parse_key)]
pub private_key: chacha20poly1305::Key,
}
type BoxResult<T> = Result<T, Box<dyn core::error::Error + Send + Sync>>;
pub fn parse_key(s: &str) -> BoxResult<chacha20poly1305::Key> {
let s = s.trim().as_bytes();
let key_bytes =
hex::decode(s).or_else(|_| base64::engine::general_purpose::STANDARD.decode(s))?;
let key_bytes: [u8; 32] = key_bytes
.try_into()
.map_err(|_v: Vec<u8>| "invalid key len")?;
Ok(key_bytes.into())
}
#[tokio::main]
async fn main() -> BoxResult<()> {
ts_cli_util::init_tracing();
let args = Args::parse();
let privkey = NodePrivateKey::read_from_bytes(args.private_key.as_bytes())
.map_err(|_| "failed reading private key")?;
eprintln!(
"my pubkey: {}",
base64::engine::general_purpose::STANDARD.encode(privkey.public_key().as_bytes())
);
let peer_key = *NodePublicKey::try_ref_from_bytes(args.peer_key.as_bytes())
.map_err(|_| "failed reading public key")?;
let mut ep = Endpoint::new(privkey.into());
let peer_id = ep
.add_peer(ts_tunnel::PeerConfig {
key: peer_key,
psk: [0; 32].into(),
})
.ok_or("couldn't add peer")?;
let sock = tokio::net::UdpSocket::bind("0.0.0.0:0").await?;
eprintln!("socket bound to {}", sock.local_addr()?.port());
let mut pinger = interval_at(Instant::now().into(), Duration::from_secs(10));
loop {
let mut buf = [0u8; 1024];
select! {
_ = pinger.tick() => {
eprintln!("sending ping to peer");
let mut packet = PacketMut::new(0);
packet.put_slice(b"test test");
let ts_tunnel::SendResult { to_peers } = ep.send([(peer_id, vec![packet])]);
for (peer_id, packets) in to_peers {
eprintln!("sending {} packets to {peer_id:?}", packets.len());
for packet in packets {
sock.send_to(packet.as_ref(), &args.endpoint).await?;
}
}
},
res = sock.recv_from(&mut buf) => {
let (n, from) = res?;
eprintln!("receive resp (len {n}, from {from})");
let buf = &buf[..n];
let ts_tunnel::RecvResult { to_peers, to_local } = ep.recv(vec![PacketMut::from(buf)]);
eprintln!(
"resp: {} packets to peers, {} to local",
to_peers.len(),
to_local.len()
);
for (peer, packets) in to_local {
eprintln!("-> local ({peer:?}) ({} packets)", packets.len());
}
for (peer, packets) in to_peers {
eprintln!("-> {peer:?} ({} packets)", packets.len());
if peer == peer_id {
for packet in packets {
sock.send_to(packet.as_ref(), &args.endpoint).await?;
}
eprintln!("sent response packets to peer");
}
}
}
now = maybe_timeout(ep.next_event()) => {
let ts_tunnel::EventResult{to_peers} = ep.dispatch_events(now);
for (peer, packets) in to_peers {
eprintln!("-> {peer:?} ({} packets)", packets.len());
if peer == peer_id {
for packet in packets {
sock.send_to(packet.as_ref(), &args.endpoint).await?;
}
eprintln!("sent response packets to peer");
}
}
}
}
}
}
async fn maybe_timeout(when: Option<TimeRange>) -> Instant {
match when {
Some(when) => {
sleep_until(when.end().into()).await;
when.end()
}
None => pending().await,
}
}