1use socket2::{Domain, Protocol, Socket, Type};
6use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, UdpSocket};
7
8pub fn bind_sockets(port: u16, want_ipv6: bool) -> std::io::Result<Vec<UdpSocket>> {
20 let mut socks = Vec::with_capacity(2);
21
22 let s4 = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP))?;
23 s4.set_reuse_address(true)?;
24 s4.bind(&SocketAddr::from((Ipv4Addr::UNSPECIFIED, port)).into())?;
25 s4.set_nonblocking(true)?;
26 socks.push(UdpSocket::from(s4));
27
28 if want_ipv6 {
29 match bind_v6(port) {
30 Ok(s6) => socks.push(s6),
31 Err(e) => tracing::info!("IPv6 listener unavailable ({e}) — continuing IPv4-only"),
32 }
33 }
34 Ok(socks)
35}
36
37fn bind_v6(port: u16) -> std::io::Result<UdpSocket> {
38 let s6 = Socket::new(Domain::IPV6, Type::DGRAM, Some(Protocol::UDP))?;
39 s6.set_reuse_address(true)?;
40 s6.set_only_v6(true)?;
41 s6.bind(&SocketAddr::from((Ipv6Addr::UNSPECIFIED, port)).into())?;
42 s6.set_nonblocking(true)?;
43 Ok(UdpSocket::from(s6))
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49
50 #[test]
51 fn binds_both_families_on_an_ephemeral_port() {
52 let socks = bind_sockets(0, true).unwrap();
53 assert!(!socks.is_empty());
54 assert!(socks[0].local_addr().unwrap().is_ipv4());
55 if socks.len() == 2 {
56 assert!(socks[1].local_addr().unwrap().is_ipv6());
57 }
58 }
59}