1use socket2::{Domain, Protocol, Socket, Type};
12use std::net::{SocketAddr, TcpListener, UdpSocket};
13
14pub fn udp(addr: SocketAddr) -> std::io::Result<UdpSocket> {
16 let domain = if addr.is_ipv4() { Domain::IPV4 } else { Domain::IPV6 };
17 let socket = Socket::new(domain, Type::DGRAM, Some(Protocol::UDP))?;
18 make_dual_stack(&socket, addr);
19 socket.bind(&addr.into())?;
20 Ok(socket.into())
21}
22
23pub fn tcp(addr: SocketAddr) -> std::io::Result<TcpListener> {
28 let domain = if addr.is_ipv4() { Domain::IPV4 } else { Domain::IPV6 };
29 let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?;
30 make_dual_stack(&socket, addr);
31 #[cfg(not(windows))]
34 socket.set_reuse_address(true)?;
35 socket.bind(&addr.into())?;
36 socket.listen(1024)?;
37 let listener: TcpListener = socket.into();
38 listener.set_nonblocking(true)?;
39 Ok(listener)
40}
41
42fn make_dual_stack(socket: &Socket, addr: SocketAddr) {
46 if addr.is_ipv6()
47 && let Err(err) = socket.set_only_v6(false)
48 {
49 tracing::warn!(%err, "failed to enable dual-stack IPv6 socket; IPv4 clients may be unreachable");
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn udp_ipv6_is_dual_stack() {
59 let socket = udp("[::]:0".parse().unwrap()).unwrap();
62 let socket = Socket::from(socket);
63 assert!(!socket.only_v6().unwrap(), "IPv6 socket should be dual-stack");
64 }
65
66 #[test]
67 fn udp_ipv4_still_binds() {
68 let socket = udp("127.0.0.1:0".parse().unwrap()).unwrap();
69 assert!(socket.local_addr().unwrap().is_ipv4());
70 }
71
72 #[test]
73 fn tcp_ipv6_is_dual_stack() {
74 let listener = tcp("[::]:0".parse().unwrap()).unwrap();
75 let socket = Socket::from(listener);
76 assert!(!socket.only_v6().unwrap(), "IPv6 listener should be dual-stack");
77 }
78}