use std::net::SocketAddr;
pub(crate) fn bind_exclusive(addr: SocketAddr) -> Result<std::net::TcpListener, String> {
use socket2::{Domain, Protocol, Socket, Type};
let domain = if addr.is_ipv6() {
Domain::IPV6
} else {
Domain::IPV4
};
let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))
.map_err(|e| format!("failed to create socket: {e}"))?;
#[cfg(unix)]
socket
.set_reuse_address(true)
.map_err(|e| format!("failed to set SO_REUSEADDR: {e}"))?;
#[cfg(windows)]
set_exclusiveaddruse(&socket)?;
socket
.set_nonblocking(true)
.map_err(|e| format!("failed to set nonblocking: {e}"))?;
socket
.bind(&addr.into())
.map_err(|e| format!("failed to bind {addr}: {e}"))?;
socket
.listen(1024)
.map_err(|e| format!("failed to listen on {addr}: {e}"))?;
Ok(std::net::TcpListener::from(socket))
}
#[cfg(feature = "mcp-server")]
pub(crate) fn bind_exclusive_to(addr: &str) -> Result<std::net::TcpListener, String> {
use std::net::ToSocketAddrs;
let addrs = addr
.to_socket_addrs()
.map_err(|e| format!("invalid address {addr}: {e}"))?;
let mut last_err = format!("no addresses resolved for {addr}");
for candidate in addrs {
match bind_exclusive(candidate) {
Ok(listener) => return Ok(listener),
Err(e) => last_err = e,
}
}
Err(last_err)
}
#[cfg(windows)]
fn set_exclusiveaddruse(socket: &socket2::Socket) -> Result<(), String> {
use std::os::windows::io::AsRawSocket;
use windows_sys::Win32::Networking::WinSock::{
SO_EXCLUSIVEADDRUSE, SOCKET, SOL_SOCKET, setsockopt,
};
let enable: i32 = 1;
let rc = unsafe {
setsockopt(
socket.as_raw_socket() as SOCKET,
SOL_SOCKET,
SO_EXCLUSIVEADDRUSE,
std::ptr::from_ref(&enable).cast::<u8>(),
size_of_val(&enable) as i32,
)
};
if rc != 0 {
return Err(format!(
"failed to set SO_EXCLUSIVEADDRUSE: {}",
std::io::Error::last_os_error()
));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Read;
use std::net::TcpStream;
use std::time::Duration;
#[test]
fn rebind_succeeds_past_time_wait() {
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let listener = bind_exclusive(addr).unwrap();
listener.set_nonblocking(false).unwrap();
let bound = listener.local_addr().unwrap();
let mut client = TcpStream::connect(bound).unwrap();
client
.set_read_timeout(Some(Duration::from_secs(5)))
.unwrap();
let (accepted, _) = listener.accept().unwrap();
drop(accepted);
let mut buf = [0u8; 1];
assert_eq!(client.read(&mut buf).unwrap(), 0);
drop(client);
drop(listener);
std::thread::sleep(Duration::from_millis(50));
bind_exclusive(bound).expect("rebind past TIME_WAIT failed");
}
}