Skip to content

Commit c6b9be6

Browse files
committed
fix: add Drop for Capture, DGRAM SO_TYPE test, fix fd leak
Signed-off-by: danbugs <danilochiarlone@gmail.com>
1 parent 22939d5 commit c6b9be6

2 files changed

Lines changed: 46 additions & 10 deletions

File tree

host/src/lib.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2808,4 +2808,29 @@ mod tests {
28082808
assert!(s.contains("\"error\""), "net_bind should be denied: {s}");
28092809
assert!(s.contains("Permission denied"), "{s}");
28102810
}
2811+
2812+
#[test]
2813+
fn net_getsockopt_returns_correct_type_for_dgram() {
2814+
let mut reg = ToolRegistry::new();
2815+
let policy = NetworkPolicy::AllowAll;
2816+
register_net_tools(&mut reg, &policy, None);
2817+
2818+
// Create a DGRAM socket (type=2).
2819+
let req = br#"{"name":"net_socket","args":{"family":2,"type":2}}"#;
2820+
let resp = std::str::from_utf8(&reg.dispatch(req)).unwrap().to_string();
2821+
let v: serde_json::Value = serde_json::from_str(&resp).unwrap();
2822+
let fd = v["result"]["fd"].as_u64().unwrap();
2823+
2824+
// Query SO_TYPE (level=1 SOL_SOCKET, optname=3 SO_TYPE).
2825+
let req =
2826+
format!(r#"{{"name":"net_getsockopt","args":{{"fd":{fd},"level":1,"optname":3}}}}"#);
2827+
let resp = std::str::from_utf8(&reg.dispatch(req.as_bytes()))
2828+
.unwrap()
2829+
.to_string();
2830+
let v: serde_json::Value = serde_json::from_str(&resp).unwrap();
2831+
assert_eq!(
2832+
v["result"]["value"], 2,
2833+
"SO_TYPE should return 2 (DGRAM), got: {resp}"
2834+
);
2835+
}
28112836
}

host/src/stderr_capture.rs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
mod imp {
99
use anyhow::Result;
1010
use nix::unistd;
11-
use std::os::fd::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd};
11+
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
1212
use std::path::Path;
1313
use std::sync::{Mutex, MutexGuard};
1414

@@ -18,7 +18,7 @@ mod imp {
1818
static STDERR_LOCK: Mutex<()> = Mutex::new(());
1919

2020
pub struct Capture {
21-
original_stderr: OwnedFd,
21+
original_stderr: Option<OwnedFd>,
2222
_guard: MutexGuard<'static, ()>,
2323
}
2424

@@ -27,23 +27,34 @@ mod imp {
2727
let guard = STDERR_LOCK
2828
.lock()
2929
.unwrap_or_else(|poisoned| poisoned.into_inner());
30-
let capture_fd = std::fs::File::create(path)?.into_raw_fd();
31-
let original_stderr_raw = unistd::dup(2)?;
32-
unistd::dup2(capture_fd, 2)?;
33-
unistd::close(capture_fd)?;
34-
let original_stderr = unsafe { OwnedFd::from_raw_fd(original_stderr_raw) };
30+
let capture_file = std::fs::File::create(path)?;
31+
let original_stderr =
32+
OwnedFd::from(unistd::dup(2).map(|fd| unsafe { OwnedFd::from_raw_fd(fd) })?);
33+
unistd::dup2(capture_file.as_raw_fd(), 2)?;
34+
// capture_file dropped here — its OwnedFd closes the fd via RAII
3535
Ok(Self {
36-
original_stderr,
36+
original_stderr: Some(original_stderr),
3737
_guard: guard,
3838
})
3939
}
4040

4141
pub fn restore(self) -> Result<()> {
42-
unistd::dup2(self.original_stderr.as_raw_fd(), 2)?;
43-
// _guard is dropped here, releasing STDERR_LOCK
42+
if let Some(ref fd) = self.original_stderr {
43+
unistd::dup2(fd.as_raw_fd(), 2)?;
44+
}
45+
// Drop restores via Drop impl if restore() wasn't called,
46+
// but explicit call lets caller handle errors.
4447
Ok(())
4548
}
4649
}
50+
51+
impl Drop for Capture {
52+
fn drop(&mut self) {
53+
if let Some(ref fd) = self.original_stderr.take() {
54+
let _ = unistd::dup2(fd.as_raw_fd(), 2);
55+
}
56+
}
57+
}
4758
}
4859

4960
#[cfg(windows)]

0 commit comments

Comments
 (0)