use std::io;
use std::net::SocketAddr;
use std::os::fd::AsFd;
use nix::sys::socket::{setsockopt, sockopt};
pub fn connected_to_itself(
socket: &impl AsFd,
local: io::Result<SocketAddr>,
peer: io::Result<SocketAddr>,
) -> bool {
let (Ok(local), Ok(peer)) = (local, peer) else {
return false;
};
if local != peer {
return false;
}
let reset = nix::libc::linger {
l_onoff: 1,
l_linger: 0,
};
let _ = setsockopt(socket, sockopt::Linger, &reset);
true
}
#[cfg(test)]
mod tests {
use std::net::{Ipv4Addr, TcpListener};
use std::time::Duration;
use super::*;
async fn self_connection() -> Option<(u16, tokio::net::TcpStream)> {
let socket = tokio::net::TcpSocket::new_v4().unwrap();
socket
.bind(SocketAddr::from((Ipv4Addr::LOCALHOST, 0)))
.unwrap();
let addr = socket.local_addr().unwrap();
let port = addr.port();
let stream = socket.connect(addr).await;
if cfg!(not(target_os = "linux")) && stream.is_err() {
return None;
}
Some((port, stream.unwrap()))
}
#[tokio::test]
async fn a_connection_to_itself_isnt_a_listener() {
let Some((_, stream)) = self_connection().await else {
return;
};
assert!(connected_to_itself(
&stream,
stream.local_addr(),
stream.peer_addr()
));
}
#[tokio::test]
async fn the_port_is_free_again_once_a_connection_to_itself_is_dropped() {
let Some((port, stream)) = self_connection().await else {
return;
};
assert!(connected_to_itself(
&stream,
stream.local_addr(),
stream.peer_addr()
));
drop(stream);
let started = std::time::Instant::now();
loop {
let socket = tokio::net::TcpSocket::new_v4().unwrap();
socket.set_reuseaddr(true).unwrap();
let bound = socket
.bind(SocketAddr::from((Ipv4Addr::LOCALHOST, port)))
.and_then(|()| socket.listen(1));
match bound {
Ok(_) => break,
Err(err) if started.elapsed() < Duration::from_secs(2) => {
assert_eq!(err.kind(), std::io::ErrorKind::AddrInUse, "{err}");
tokio::time::sleep(Duration::from_millis(10)).await;
}
Err(err) => panic!("port {port} still taken: {err}"),
}
}
}
#[test]
fn other_connections_are_left_alone() {
let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).unwrap();
let stream = std::net::TcpStream::connect(listener.local_addr().unwrap()).unwrap();
assert!(!connected_to_itself(
&stream,
stream.local_addr(),
stream.peer_addr()
));
}
}