use tokio::io::{AsyncRead, AsyncWrite};
pub mod adapter;
pub mod handle;
pub mod packets;
pub mod stream;
#[cfg(feature = "pcap")]
pub type PcapLog = std::sync::Arc<tokio::sync::Mutex<tokio::fs::File>>;
#[cfg(not(feature = "pcap"))]
#[derive(Debug, Clone, Copy)]
pub struct PcapLog;
#[allow(unused_imports)]
pub(crate) mod time {
#[cfg(not(target_arch = "wasm32"))]
pub use tokio::time::*;
#[cfg(target_arch = "wasm32")]
pub use wasmtimer::tokio::*;
#[cfg(target_arch = "wasm32")]
pub use wasmtimer::std::Instant;
}
#[allow(dead_code)]
pub(crate) fn spawn<F>(fut: F)
where
F: std::future::Future<Output = ()> + Send + 'static,
{
#[cfg(not(target_arch = "wasm32"))]
{
tokio::spawn(fut);
}
#[cfg(target_arch = "wasm32")]
{
wasm_bindgen_futures::spawn_local(fut);
}
}
pub trait ReadWrite: AsyncRead + AsyncWrite + Unpin + Send + Sync + std::fmt::Debug {}
impl<T: AsyncRead + AsyncWrite + Unpin + Send + Sync + std::fmt::Debug> ReadWrite for T {}
#[cfg(feature = "pcap")]
pub(crate) fn log_packet(file: &PcapLog, packet: &[u8]) {
use std::time::{SystemTime, UNIX_EPOCH};
use tokio::io::AsyncWriteExt;
use tracing::trace;
trace!("Logging {} byte packet", packet.len());
let packet = packet.to_vec();
let file = file.to_owned();
let now = SystemTime::now();
tokio::task::spawn(async move {
let mut file = file.lock().await;
file.write_all(&(now.duration_since(UNIX_EPOCH).unwrap().as_secs() as u32).to_le_bytes())
.await
.unwrap();
let micros = now.duration_since(UNIX_EPOCH).unwrap().as_micros() % 1_000_000_000;
file.write_all(&(micros as u32).to_le_bytes())
.await
.unwrap();
file.write_all(&(packet.len() as u32).to_le_bytes())
.await
.unwrap();
file.write_all(&(packet.len() as u32).to_le_bytes())
.await
.unwrap();
file.write_all(&packet).await.unwrap();
});
}
#[cfg(not(feature = "pcap"))]
#[allow(dead_code)]
pub(crate) fn log_packet(_file: &PcapLog, _packet: &[u8]) {}
#[cfg(test)]
mod tests {
use std::{
net::{IpAddr, Ipv6Addr},
str::FromStr,
};
use super::*;
use adapter::Adapter;
use std::{
pin::Pin,
task::{Context, Poll},
};
use stream::AdapterStream;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tun_rs::DeviceBuilder;
use bytes::BytesMut;
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tun_rs::AsyncDevice;
pub struct AsyncDeviceWrapper {
device: AsyncDevice,
buffer: BytesMut,
}
impl std::fmt::Debug for AsyncDeviceWrapper {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AsyncDeviceWrapper")
.field("buffer", &self.buffer)
.finish()
}
}
impl AsyncRead for AsyncDeviceWrapper {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
let this = self.get_mut();
if !this.buffer.is_empty() {
let bytes_to_copy = std::cmp::min(this.buffer.len(), buf.remaining());
let data_to_copy = this.buffer.split_to(bytes_to_copy);
buf.put_slice(&data_to_copy);
return Poll::Ready(Ok(()));
}
let mut temp_buf = vec![0u8; 4096];
match this.device.poll_recv(cx, &mut temp_buf) {
Poll::Ready(Ok(n)) => {
if n > 0 {
let bytes_to_copy = std::cmp::min(n, buf.remaining());
buf.put_slice(&temp_buf[..bytes_to_copy]);
if n > bytes_to_copy {
this.buffer.extend_from_slice(&temp_buf[bytes_to_copy..n]);
}
Poll::Ready(Ok(()))
} else {
Poll::Ready(Ok(()))
}
}
Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
Poll::Pending => Poll::Pending,
}
}
}
impl AsyncWrite for AsyncDeviceWrapper {
fn poll_write(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &[u8],
) -> std::task::Poll<Result<usize, std::io::Error>> {
self.device.poll_send(cx, buf)
}
fn poll_flush(
self: std::pin::Pin<&mut Self>,
_cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), std::io::Error>> {
std::task::Poll::Ready(Ok(()))
}
fn poll_shutdown(
self: std::pin::Pin<&mut Self>,
_cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), std::io::Error>> {
std::task::Poll::Ready(Ok(()))
}
}
const SERVER_PORT: u16 = 5555;
#[tokio::test]
async fn local_tcp() {
let our_ip = Ipv6Addr::from_str("fd12:3456:789a::1").unwrap();
let their_ip = Ipv6Addr::from_str("fd12:3456:789a::2").unwrap();
let dev = DeviceBuilder::new()
.ipv6(their_ip, "ffff:ffff:ffff:ffff::")
.mtu(1420)
.build_async()
.expect("Failed to create tunnel. Are you root?");
println!("Created tunnel [{:?}] {}", dev.name(), their_ip);
let mut adapter = Adapter::new(
Box::new(AsyncDeviceWrapper {
device: dev,
buffer: BytesMut::new(),
}),
IpAddr::V6(our_ip),
IpAddr::V6(their_ip),
);
adapter.pcap("./local_tcp.pcap").await.expect("no pcap");
tokio::task::spawn(async move {
let listener = tokio::net::TcpListener::bind(format!("[::0]:{SERVER_PORT}"))
.await
.unwrap();
while let Ok((mut stream, addr)) = listener.accept().await {
println!("Accepted connection from {addr:?}");
tokio::task::spawn(async move {
loop {
let mut buf = [0; 1024];
let read_len = stream.read(&mut buf).await.unwrap();
stream.write_all(&buf[..read_len]).await.unwrap();
}
});
}
});
println!("Attach Wireshark, press enter to continue\n");
let mut buf = Vec::new();
let _ = tokio::io::stdin().read(&mut buf).await.unwrap();
let mut stream = match AdapterStream::connect(&mut adapter, SERVER_PORT).await {
Ok(s) => s,
Err(e) => {
println!("no connect: {e:?}");
return;
}
};
if let Err(e) = stream.write_all(&[1, 2, 3, 4, 5]).await {
println!("no send: {e:?}");
} else {
let mut buf = [0u8; 4];
match stream.read_exact(&mut buf).await {
Ok(_) => println!("recv'd {buf:?}"),
Err(e) => println!("no recv: {e:?}"),
}
}
if let Err(e) = stream.write_all(&[69, 69, 42, 0, 1]).await {
println!("no send: {e:?}");
} else {
let mut buf = [0u8; 6];
match stream.read_exact(&mut buf).await {
Ok(_) => println!("recv'd {buf:?}"),
Err(e) => println!("no recv: {e:?}"),
}
}
if let Err(e) = stream.close().await {
println!("no close: {e:?}");
}
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
println!("\n\npress enter");
let mut buf = Vec::new();
let _ = tokio::io::stdin().read(&mut buf).await.unwrap();
}
}