From b6f943a77c65454c4d9e70d03da5e9ea846422be Mon Sep 17 00:00:00 2001 From: John Coffey Date: Sat, 26 Sep 2026 01:36:39 -0700 Subject: [PATCH] Don't listen on a socket whose bind failed When a listener couldn't bind its address (a port below 1024 without root, a port already in use, or the legacy-protocols switch putting a listener back after privileges were dropped), the bind error was reported but the socket was still passed to listen(). The kernel then bound it itself, to a random port on every interface, and the server logged the listener as started on the port it was configured with. listen() now refuses a socket that isn't bound, so the listener is reported with a listen error and skipped, and nothing opens anywhere unexpected. --- crates/common/src/network/listen.rs | 41 +++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/crates/common/src/network/listen.rs b/crates/common/src/network/listen.rs index 516f68f..68ba433 100644 --- a/crates/common/src/network/listen.rs +++ b/crates/common/src/network/listen.rs @@ -421,6 +421,15 @@ impl Listeners { impl TcpListener { pub fn listen(self) -> Result { + // inbuxa: a socket whose bind failed is still unbound, and listen() + // on it makes the kernel pick a random port on every interface + if !self + .socket + .local_addr() + .is_ok_and(|bound| bound.port() != 0) + { + return Err(format!("Not listening on {}: it isn't bound", self.addr)); + } self.socket .listen(self.backlog.unwrap_or(1024)) .map_err(|err| format!("Failed to listen on {}: {}", self.addr, err)) @@ -485,3 +494,35 @@ impl ServerInstance { } } } + +#[cfg(test)] +mod tests { + use crate::config::server::TcpListener; + use tokio::net::TcpSocket; + + fn listener(socket: TcpSocket, addr: &str) -> TcpListener { + TcpListener { + socket, + addr: addr.parse().unwrap(), + backlog: None, + ttl: None, + nodelay: true, + } + } + + #[tokio::test] + async fn an_unbound_socket_is_not_listened_on() { + // What a failed bind leaves behind: listening would pick a random port + let socket = TcpSocket::new_v4().unwrap(); + let err = listener(socket, "0.0.0.0:25").listen().unwrap_err(); + assert!(err.contains("isn't bound"), "{err}"); + } + + #[tokio::test] + async fn a_bound_socket_listens_even_on_port_zero() { + let socket = TcpSocket::new_v4().unwrap(); + socket.bind("127.0.0.1:0".parse().unwrap()).unwrap(); + let bound = listener(socket, "127.0.0.1:0").listen().unwrap(); + assert_ne!(bound.local_addr().unwrap().port(), 0); + } +}