diff --git a/lib/proxy/control.rs b/lib/proxy/control.rs index cc951c7..2307f85 100644 --- a/lib/proxy/control.rs +++ b/lib/proxy/control.rs @@ -15,6 +15,7 @@ use smoltcp::wire::Ipv4Address; use std::collections::HashSet; use std::io::{self, ErrorKind, Read, Write}; use std::mem::{size_of, zeroed}; +use std::net::Shutdown; use std::os::fd::{AsRawFd, FromRawFd, RawFd}; use std::os::unix::net::UnixStream; @@ -311,6 +312,12 @@ impl Control { Ok(!self.input_closed || !self.output.is_empty()) } + pub(super) fn shutdown(&self) -> Result<()> { + self.stream + .shutdown(Shutdown::Both) + .context("failed to shut down the control socket") + } + fn process_input(&mut self, policy: &mut Policy) -> Result<()> { loop { if self.discarding_input { @@ -1138,4 +1145,20 @@ mod tests { drop(control); assert!(unsafe { libc::fcntl(stream.as_raw_fd(), libc::F_GETFD) != -1 }); } + + #[test] + fn shutdown_signals_eof_while_the_original_descriptor_remains_open() { + let (mut client, server) = UnixStream::pair().unwrap(); + client + .set_read_timeout(Some(Duration::from_secs(1))) + .unwrap(); + let control = Control::new(server.as_raw_fd()).unwrap(); + + control.shutdown().unwrap(); + drop(control); + + assert!(unsafe { libc::fcntl(server.as_raw_fd(), libc::F_GETFD) != -1 }); + let mut response = [0; 1]; + assert_eq!(client.read(&mut response).unwrap(), 0); + } } diff --git a/lib/proxy/mod.rs b/lib/proxy/mod.rs index c3f73c5..fdddf17 100644 --- a/lib/proxy/mod.rs +++ b/lib/proxy/mod.rs @@ -221,7 +221,11 @@ impl Proxy<'_> { log::warn!("failed to remove Softnet control socket from the poller: {err:#}"); } - self.control = None; + if let Some(control) = self.control.take() + && let Err(err) = control.shutdown() + { + log::warn!("failed to shut down Softnet control socket: {err:#}"); + } } }