diff --git a/src/net/udp.rs b/src/net/udp.rs index 436b4cc12..c5c3ba92f 100644 --- a/src/net/udp.rs +++ b/src/net/udp.rs @@ -511,6 +511,12 @@ impl UdpSocket { self.inner.leave_multicast_v6(multiaddr, interface) } + /// Get the value of the `IPV6_V6ONLY` option on this socket. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn only_v6(&self) -> io::Result { + sys::udp::only_v6(&self.inner) + } + /// Get the value of the `SO_ERROR` option on this socket. /// /// This will retrieve the stored error in the underlying socket, clearing diff --git a/src/sys/shell/udp.rs b/src/sys/shell/udp.rs index 3ff162552..48ccac740 100644 --- a/src/sys/shell/udp.rs +++ b/src/sys/shell/udp.rs @@ -4,3 +4,7 @@ use std::net::{self, SocketAddr}; pub fn bind(_: SocketAddr) -> io::Result { os_required!() } + +pub(crate) fn only_v6(_: &net::UdpSocket) -> io::Result { + os_required!() +} diff --git a/src/sys/unix/udp.rs b/src/sys/unix/udp.rs index e9c4d4cd2..5a97cbd89 100644 --- a/src/sys/unix/udp.rs +++ b/src/sys/unix/udp.rs @@ -1,8 +1,9 @@ use crate::sys::unix::net::{new_ip_socket, socket_addr}; use std::io; +use std::mem; use std::net::{self, SocketAddr}; -use std::os::unix::io::FromRawFd; +use std::os::unix::io::{AsRawFd, FromRawFd}; pub fn bind(addr: SocketAddr) -> io::Result { // Gives a warning for non Apple platforms. @@ -21,3 +22,18 @@ pub fn bind(addr: SocketAddr) -> io::Result { .map(|_| unsafe { net::UdpSocket::from_raw_fd(socket) }) }) } + +pub(crate) fn only_v6(socket: &net::UdpSocket) -> io::Result { + let mut optval: libc::c_int = 0; + let mut optlen = mem::size_of::() as libc::socklen_t; + + syscall!(getsockopt( + socket.as_raw_fd(), + libc::IPPROTO_IPV6, + libc::IPV6_V6ONLY, + &mut optval as *mut _ as *mut _, + &mut optlen, + ))?; + + Ok(optval != 0) +} diff --git a/src/sys/windows/udp.rs b/src/sys/windows/udp.rs index ba2aeac1d..825ecccff 100644 --- a/src/sys/windows/udp.rs +++ b/src/sys/windows/udp.rs @@ -1,9 +1,13 @@ use std::io; +use std::mem::{self, MaybeUninit}; use std::net::{self, SocketAddr}; -use std::os::windows::io::FromRawSocket; +use std::os::windows::io::{AsRawSocket, FromRawSocket}; use std::os::windows::raw::SOCKET as StdSocket; // winapi uses usize, stdlib uses u32/u64. -use winapi::um::winsock2::{bind as win_bind, closesocket, SOCKET_ERROR, SOCK_DGRAM}; +use winapi::ctypes::c_int; +use winapi::shared::ws2def::IPPROTO_IPV6; +use winapi::shared::ws2ipdef::IPV6_V6ONLY; +use winapi::um::winsock2::{bind as win_bind, closesocket, getsockopt, SOCKET_ERROR, SOCK_DGRAM}; use crate::sys::windows::net::{init, new_ip_socket, socket_addr}; @@ -25,3 +29,25 @@ pub fn bind(addr: SocketAddr) -> io::Result { .map(|_| unsafe { net::UdpSocket::from_raw_socket(socket as StdSocket) }) }) } + +pub(crate) fn only_v6(socket: &net::UdpSocket) -> io::Result { + let mut optval: MaybeUninit = MaybeUninit::uninit(); + let mut optlen = mem::size_of::() as c_int; + + syscall!( + getsockopt( + socket.as_raw_socket() as usize, + IPPROTO_IPV6 as c_int, + IPV6_V6ONLY as c_int, + optval.as_mut_ptr().cast(), + &mut optlen, + ), + PartialEq::eq, + SOCKET_ERROR + )?; + + debug_assert_eq!(optlen as usize, mem::size_of::()); + // Safety: `getsockopt` initialised `optval` for us. + let optval = unsafe { optval.assume_init() }; + Ok(optval != 0) +}