Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ etherparse = { version = "0.21", default-features = false, features = ["std"] }
log = { version = "0.4", default-features = false }
rand = { version = "0.10", default-features = false, features = ["thread_rng"] }
thiserror = { version = "2.0", default-features = false }
tokio = { version = "1.53", default-features = false, features = [
tokio = { version = "1.54", default-features = false, features = [
"sync",
"rt",
"time",
Expand All @@ -29,8 +29,9 @@ clap = { version = "4.6", default-features = false, features = ["derive"] }
criterion = { version = "0.8" } # Benchmarks
dotenvy = "0.15"
env_logger = "0.11"
tokio = { version = "1.53", default-features = false, features = [
tokio = { version = "1.54", default-features = false, features = [
"rt-multi-thread",
"test-util",
] }
tun = { version = "0.8", default-features = false, features = ["async"] }
udp-stream = { version = "0.0", default-features = false }
Expand Down
7 changes: 5 additions & 2 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let dll_path = get_wintun_bin_relative_path()?;
let src_path = crate_dir.join(dll_path);

let dst_path = cargo_target_dir.join("examples/wintun.dll");
let examples_dir = cargo_target_dir.join("examples");
let dst_path = examples_dir.join("wintun.dll");

// Copy to the target directory
// Cargo creates `examples/` only when it builds an example, which a
// dependent crate or a `cargo check` never does.
std::fs::create_dir_all(&examples_dir)?;
std::fs::copy(src_path, &dst_path)?;

// Set the modified time to the current time, or the publishing process will fail.
Expand Down
35 changes: 34 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,18 @@ fn run<Device: AsyncRead + AsyncWrite + Unpin + Send + 'static>(
tokio::spawn(async move {
loop {
select! {
Ok(n) = device.read(&mut buffer) => {
read_result = device.read(&mut buffer) => {
let n = match read_result {
Ok(0) => {
log::info!("Device EOF, stopping IP stack");
return Ok(());
}
Ok(n) => n,
Err(e) => {
log::error!("Device read error: {e}");
return Err(e.into());
}
};
if let Err(e) = process_device_read(&buffer[offset..n], &mut sessions, &session_remove_tx, &up_pkt_sender, &config, &accept_sender).await {
let io_err: std::io::Error = e.into();
if io_err.kind() == std::io::ErrorKind::ConnectionRefused {
Expand Down Expand Up @@ -441,3 +452,25 @@ async fn process_upstream_recv<Device: AsyncWrite + Unpin + 'static>(

Ok(())
}

#[cfg(test)]
mod tests {
use std::time::Duration;

use tokio::{io::duplex, time::timeout};

use super::*;

#[tokio::test]
async fn device_eof_closes_accept_channel() {
let (device, peer) = duplex(64);
let mut ip_stack = IpStack::new(IpStackConfig::default(), device);

drop(peer);

let accept_result = timeout(Duration::from_secs(1), ip_stack.accept())
.await
.expect("accept should not hang after device EOF");
assert!(matches!(accept_result, Err(IpStackError::AcceptError)));
}
}
Loading
Loading