From f2e0ce3099645c056362c50c36316d5caf0eccff Mon Sep 17 00:00:00 2001 From: LinkTed Date: Sun, 28 Feb 2021 13:25:40 +0100 Subject: [PATCH 1/2] unix: Extend UdpSocket to send and receive TTL Add the function recv_vectored_with_ancillary_from, recv_vectored_with_ancillary, send_vectored_with_ancillary_to and send_vectored_with_ancillary to UdpSocket for Unix platforms. Also add set_recvttl and recvttl to UdpSocket to tell the kernel to receive the TTL. Therefore, rename the Ancillary(Data) to UnixAncillary(Data) for the UnixDatagram and UnixStream and also add the IpAncillary(Data) for UdpSocket. --- .../std/src/os/unix/net/ancillary/inner.rs | 262 +++++++++++++ library/std/src/os/unix/net/ancillary/ip.rs | 269 +++++++++++++ library/std/src/os/unix/net/ancillary/mod.rs | 9 + .../net/{ancillary.rs => ancillary/unix.rs} | 370 +++++------------- library/std/src/os/unix/net/datagram.rs | 44 ++- library/std/src/os/unix/net/mod.rs | 2 + library/std/src/os/unix/net/stream.rs | 24 +- library/std/src/os/unix/net/tests.rs | 84 ++-- library/std/src/os/unix/net/udp.rs | 216 ++++++++++ library/std/src/sys/unix/net.rs | 11 + library/std/src/sys_common/net.rs | 100 +++++ 11 files changed, 1065 insertions(+), 326 deletions(-) create mode 100644 library/std/src/os/unix/net/ancillary/inner.rs create mode 100644 library/std/src/os/unix/net/ancillary/ip.rs create mode 100644 library/std/src/os/unix/net/ancillary/mod.rs rename library/std/src/os/unix/net/{ancillary.rs => ancillary/unix.rs} (50%) create mode 100644 library/std/src/os/unix/net/udp.rs diff --git a/library/std/src/os/unix/net/ancillary/inner.rs b/library/std/src/os/unix/net/ancillary/inner.rs new file mode 100644 index 0000000000000..614b1497f17bd --- /dev/null +++ b/library/std/src/os/unix/net/ancillary/inner.rs @@ -0,0 +1,262 @@ +use crate::{ + convert::TryFrom, + io::{self, IoSlice, IoSliceMut}, + marker::PhantomData, + mem::{size_of, zeroed}, + ptr::{eq, read_unaligned}, + slice::from_raw_parts, + sys::net::Socket, +}; + +// FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here? +#[cfg(all(doc, not(target_os = "linux"), not(target_os = "android")))] +#[allow(non_camel_case_types)] +mod libc { + pub use libc::c_int; + pub struct cmsghdr; +} + +impl Socket { + pub(super) unsafe fn recv_vectored_with_ancillary_from( + &self, + msg_name: *mut libc::c_void, + msg_namelen: libc::socklen_t, + bufs: &mut [IoSliceMut<'_>], + ancillary: &mut Ancillary<'_>, + ) -> io::Result<(usize, bool, libc::socklen_t)> { + let mut msg: libc::msghdr = zeroed(); + msg.msg_name = msg_name; + msg.msg_namelen = msg_namelen; + msg.msg_iov = bufs.as_mut_ptr().cast(); + msg.msg_iovlen = bufs.len() as _; + msg.msg_controllen = ancillary.buffer.len() as _; + // macos requires that the control pointer is null when the len is 0. + if msg.msg_controllen > 0 { + msg.msg_control = ancillary.buffer.as_mut_ptr().cast(); + } + + let count = self.recv_msg(&mut msg)?; + + ancillary.length = msg.msg_controllen as usize; + ancillary.truncated = msg.msg_flags & libc::MSG_CTRUNC == libc::MSG_CTRUNC; + + let truncated = msg.msg_flags & libc::MSG_TRUNC == libc::MSG_TRUNC; + + Ok((count, truncated, msg.msg_namelen)) + } + + pub(super) unsafe fn send_vectored_with_ancillary_to( + &self, + msg_name: *mut libc::c_void, + msg_namelen: libc::socklen_t, + bufs: &[IoSlice<'_>], + ancillary: &mut Ancillary<'_>, + ) -> io::Result { + let mut msg: libc::msghdr = zeroed(); + msg.msg_name = msg_name; + msg.msg_namelen = msg_namelen; + msg.msg_iov = bufs.as_ptr() as *mut _; + msg.msg_iovlen = bufs.len() as _; + msg.msg_controllen = ancillary.length as _; + // macos requires that the control pointer is null when the len is 0. + if msg.msg_controllen > 0 { + msg.msg_control = ancillary.buffer.as_mut_ptr().cast(); + } + + ancillary.truncated = false; + + self.send_msg(&mut msg) + } +} + +#[derive(Debug)] +pub(crate) struct Ancillary<'a> { + buffer: &'a mut [u8], + length: usize, + truncated: bool, +} + +impl<'a> Ancillary<'a> { + pub(super) fn new(buffer: &'a mut [u8]) -> Self { + Ancillary { buffer, length: 0, truncated: false } + } +} + +impl Ancillary<'_> { + pub(super) fn add_to_ancillary_data( + &mut self, + source: &[T], + cmsg_level: libc::c_int, + cmsg_type: libc::c_int, + ) -> bool { + self.truncated = false; + + let source_len = if let Some(source_len) = source.len().checked_mul(size_of::()) { + if let Ok(source_len) = u32::try_from(source_len) { + source_len + } else { + return false; + } + } else { + return false; + }; + + unsafe { + let additional_space = libc::CMSG_SPACE(source_len) as usize; + + let new_length = if let Some(new_length) = additional_space.checked_add(self.length) { + new_length + } else { + return false; + }; + + if new_length > self.buffer.len() { + return false; + } + + self.buffer[self.length..new_length].fill(0); + + self.length = new_length; + + let mut msg: libc::msghdr = zeroed(); + msg.msg_control = self.buffer.as_mut_ptr().cast(); + msg.msg_controllen = self.length as _; + + let mut cmsg = libc::CMSG_FIRSTHDR(&msg); + let mut previous_cmsg = cmsg; + while !cmsg.is_null() { + previous_cmsg = cmsg; + cmsg = libc::CMSG_NXTHDR(&msg, cmsg); + + // Most operating systems, but not Linux or emscripten, return the previous pointer + // when its length is zero. Therefore, check if the previous pointer is the same as + // the current one. + if eq(cmsg, previous_cmsg) { + break; + } + } + + if previous_cmsg.is_null() { + return false; + } + + (*previous_cmsg).cmsg_level = cmsg_level; + (*previous_cmsg).cmsg_type = cmsg_type; + (*previous_cmsg).cmsg_len = libc::CMSG_LEN(source_len) as _; + + let data = libc::CMSG_DATA(previous_cmsg).cast(); + + libc::memcpy(data, source.as_ptr().cast(), source_len as usize); + } + true + } + + pub(super) fn capacity(&self) -> usize { + self.buffer.len() + } + + pub(super) fn is_empty(&self) -> bool { + self.length == 0 + } + + pub(super) fn len(&self) -> usize { + self.length + } + + pub(super) fn messages(&self) -> Messages<'_, T> { + Messages { buffer: &self.buffer[..self.length], current: None, phantom: PhantomData {} } + } + + pub(super) fn truncated(&self) -> bool { + self.truncated + } + + pub(super) fn clear(&mut self) { + self.length = 0; + self.truncated = false; + } +} + +pub(super) struct AncillaryDataIter<'a, T> { + data: &'a [u8], + phantom: PhantomData, +} + +impl<'a, T> AncillaryDataIter<'a, T> { + /// Create `AncillaryDataIter` struct to iterate through the data unit in the control message. + /// + /// # Safety + /// + /// `data` must contain a valid control message. + pub(super) unsafe fn new(data: &'a [u8]) -> AncillaryDataIter<'a, T> { + AncillaryDataIter { data, phantom: PhantomData } + } +} + +impl<'a, T> Iterator for AncillaryDataIter<'a, T> { + type Item = T; + + fn next(&mut self) -> Option { + if size_of::() <= self.data.len() { + unsafe { + let unit = read_unaligned(self.data.as_ptr().cast()); + self.data = &self.data[size_of::()..]; + Some(unit) + } + } else { + None + } + } +} + +/// The error type which is returned from parsing the type a control message. +#[non_exhaustive] +#[derive(Debug)] +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +pub enum AncillaryError { + Unknown { cmsg_level: i32, cmsg_type: i32 }, +} + +/// Return the data of `cmsghdr` as a `u8` slice. +pub(super) unsafe fn get_data_from_cmsghdr(cmsg: &libc::cmsghdr) -> &[u8] { + let cmsg_len_zero = libc::CMSG_LEN(0) as usize; + let data_len = (*cmsg).cmsg_len as usize - cmsg_len_zero; + let data = libc::CMSG_DATA(cmsg).cast(); + from_raw_parts(data, data_len) +} + +/// This struct is used to iterate through the control messages. +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +pub struct Messages<'a, T> { + buffer: &'a [u8], + current: Option<&'a libc::cmsghdr>, + phantom: PhantomData, +} + +impl<'a, T> Messages<'a, T> { + pub(super) unsafe fn next_cmsghdr(&mut self) -> Option<&'a libc::cmsghdr> { + let mut msg: libc::msghdr = zeroed(); + msg.msg_control = self.buffer.as_ptr() as *mut _; + msg.msg_controllen = self.buffer.len() as _; + + let cmsg = if let Some(current) = self.current { + libc::CMSG_NXTHDR(&msg, current) + } else { + libc::CMSG_FIRSTHDR(&msg) + }; + + let cmsg = cmsg.as_ref()?; + + // Most operating systems, but not Linux or emscripten, return the previous pointer + // when its length is zero. Therefore, check if the previous pointer is the same as + // the current one. + if let Some(current) = self.current { + if eq(current, cmsg) { + return None; + } + } + + self.current = Some(cmsg); + Some(cmsg) + } +} diff --git a/library/std/src/os/unix/net/ancillary/ip.rs b/library/std/src/os/unix/net/ancillary/ip.rs new file mode 100644 index 0000000000000..b338b671eed22 --- /dev/null +++ b/library/std/src/os/unix/net/ancillary/ip.rs @@ -0,0 +1,269 @@ +use super::inner::{get_data_from_cmsghdr, Ancillary, AncillaryDataIter, AncillaryError, Messages}; +use crate::{ + io::{self, IoSlice, IoSliceMut}, + mem::{size_of, zeroed}, + net::SocketAddr, + slice::from_ref, + sys::net::Socket, + sys_common::{net::sockaddr_to_addr, IntoInner}, +}; + +// FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here? +#[cfg(all(doc, not(target_os = "linux"), not(target_os = "android")))] +#[allow(non_camel_case_types)] +mod libc { + pub use libc::c_int; + pub struct cmsghdr; +} + +impl Socket { + pub(crate) fn recv_vectored_with_ancillary_from_udp( + &self, + bufs: &mut [IoSliceMut<'_>], + ancillary: &mut IpAncillary<'_>, + ) -> io::Result<(usize, bool, io::Result)> { + unsafe { + let mut msg_name: libc::sockaddr_storage = zeroed(); + let (count, truncated, msg_namelen) = self.recv_vectored_with_ancillary_from( + &mut msg_name as *mut _ as *mut libc::c_void, + size_of::() as libc::socklen_t, + bufs, + &mut ancillary.inner, + )?; + let addr = sockaddr_to_addr(&msg_name, msg_namelen as usize); + + Ok((count, truncated, addr)) + } + } + + pub(crate) fn send_vectored_with_ancillary_to_udp( + &self, + dst: Option<&SocketAddr>, + bufs: &[IoSlice<'_>], + ancillary: &mut IpAncillary<'_>, + ) -> io::Result { + unsafe { + let (msg_name, msg_namelen) = + if let Some(dst) = dst { dst.into_inner() } else { (zeroed(), 0) }; + self.send_vectored_with_ancillary_to( + msg_name as *mut libc::c_void, + msg_namelen, + bufs, + &mut ancillary.inner, + ) + } + } +} + +/// This enum represent one control message of variable type for `IPPROTO_IP`. +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +#[non_exhaustive] +pub enum IpAncillaryData { + Ttl(u8), +} + +impl IpAncillaryData { + /// Convert the a `libc::c_int` to `u8` + /// + /// # Safety + /// + /// `data` must contain at least one `libc::c_int`. + unsafe fn as_u8(data: &[u8]) -> u8 { + let mut ancillary_data_iter = AncillaryDataIter::::new(data); + if let Some(u) = ancillary_data_iter.next() { u as u8 } else { 0 } + } + + /// Create a `AncillaryData::Ttl` variant. + /// + /// # Safety + /// + /// `data` must contain a valid control message and the control message must be type of + /// `IPPROTO_IP` and level of `IP_TTL`. + unsafe fn as_ttl(data: &[u8]) -> Self { + let ttl = IpAncillaryData::as_u8(data); + IpAncillaryData::Ttl(ttl) + } + + unsafe fn try_from(cmsg: &libc::cmsghdr) -> Result { + let data = get_data_from_cmsghdr(cmsg); + + match (*cmsg).cmsg_level { + libc::IPPROTO_IP => match (*cmsg).cmsg_type { + libc::IP_TTL => Ok(IpAncillaryData::as_ttl(data)), + cmsg_type => { + Err(AncillaryError::Unknown { cmsg_level: libc::IPPROTO_IP, cmsg_type }) + } + }, + cmsg_level => Err(AncillaryError::Unknown { cmsg_level, cmsg_type: (*cmsg).cmsg_type }), + } + } +} + +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +impl<'a> Iterator for Messages<'a, IpAncillaryData> { + type Item = Result; + + fn next(&mut self) -> Option { + unsafe { + let cmsg = self.next_cmsghdr()?; + let ancillary_result = IpAncillaryData::try_from(cmsg); + Some(ancillary_result) + } + } +} + +/// A net IP Ancillary data struct. +/// +/// # Example +/// ```no_run +/// #![feature(unix_socket_ancillary_data)] +/// use std::net::UdpSocket; +/// use std::os::unix::net::{IpAncillary, IpAncillaryData}; +/// use std::io::IoSliceMut; +/// +/// fn main() -> std::io::Result<()> { +/// let sock = UdpSocket::bind("127.0.0.1:34254")?; +/// +/// let mut ancillary_buffer = [0; 128]; +/// let mut ancillary = IpAncillary::new(&mut ancillary_buffer[..]); +/// +/// let mut buf = [1; 8]; +/// let mut bufs = &mut [IoSliceMut::new(&mut buf[..])][..]; +/// sock.recv_vectored_with_ancillary(bufs, &mut ancillary)?; +/// +/// for ancillary_result in ancillary.messages() { +/// if let IpAncillaryData::Ttl(ttl) = ancillary_result.unwrap() { +/// println!("receive packet with TTL: {}", ttl); +/// } +/// } +/// Ok(()) +/// } +/// ``` +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +#[derive(Debug)] +pub struct IpAncillary<'a> { + pub(crate) inner: Ancillary<'a>, +} + +impl<'a> IpAncillary<'a> { + /// Create an ancillary data with the given buffer. + /// + /// # Example + /// + /// ```no_run + /// # #![allow(unused_mut)] + /// #![feature(unix_socket_ancillary_data)] + /// use std::os::unix::net::IpAncillary; + /// let mut ancillary_buffer = [0; 128]; + /// let mut ancillary = IpAncillary::new(&mut ancillary_buffer[..]); + /// ``` + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn new(buffer: &'a mut [u8]) -> Self { + IpAncillary { inner: Ancillary::new(buffer) } + } + + /// Returns the capacity of the buffer. + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn capacity(&self) -> usize { + self.inner.capacity() + } + + /// Returns `true` if the ancillary data is empty. + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + /// Returns the number of used bytes. + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn len(&self) -> usize { + self.inner.len() + } + + /// Returns the iterator of the control messages. + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn messages(&self) -> Messages<'_, IpAncillaryData> { + self.inner.messages() + } + + /// Add TTL to the ancillary data. + /// + /// The function returns `true` if there was enough space in the buffer. + /// If there was not enough space then no file descriptors was appended. + /// Technically, that means this operation adds a control message with the level `IPPROTO_IP` + /// and type `IP_TTL`. + /// + /// # Example + /// ```no_run + /// #![feature(unix_socket_ancillary_data)] + /// use std::io::IoSlice; + /// use std::net::UdpSocket; + /// use std::os::unix::net::IpAncillary; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UdpSocket::bind("127.0.0.1:34254")?; + /// sock.connect("127.0.0.1:41203")?; + /// let buf1 = [1; 8]; + /// let buf2 = [2; 16]; + /// let buf3 = [3; 8]; + /// let bufs = &[ + /// IoSlice::new(&buf1), + /// IoSlice::new(&buf2), + /// IoSlice::new(&buf3), + /// ][..]; + /// let mut ancillary_buffer = [0; 128]; + /// let mut ancillary = IpAncillary::new(&mut ancillary_buffer[..]); + /// ancillary.add_ttl(20); + /// sock.send_vectored_with_ancillary(bufs, &mut ancillary) + /// .expect("send_vectored_with_ancillary function failed"); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn add_ttl(&mut self, ttl: u8) -> bool { + let ttl: libc::c_int = ttl as libc::c_int; + self.inner.add_to_ancillary_data(from_ref(&ttl), libc::IPPROTO_IP, libc::IP_TTL) + } + + /// Clears the ancillary data, removing all values. + /// + /// # Example + /// + /// ```no_run + /// #![feature(unix_socket_ancillary_data)] + /// use std::net::UdpSocket; + /// use std::os::unix::net::{IpAncillary, IpAncillaryData}; + /// use std::io::IoSliceMut; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UdpSocket::bind("127.0.0.1:34254")?; + /// + /// let mut ancillary_buffer = [0; 128]; + /// let mut ancillary = IpAncillary::new(&mut ancillary_buffer[..]); + /// + /// let mut buf = [1; 8]; + /// let mut bufs = &mut [IoSliceMut::new(&mut buf[..])][..]; + /// + /// sock.recv_vectored_with_ancillary(bufs, &mut ancillary)?; + /// for ancillary_result in ancillary.messages() { + /// if let IpAncillaryData::Ttl(ttl) = ancillary_result.unwrap() { + /// println!("receive packet with TTL: {}", ttl); + /// } + /// } + /// + /// ancillary.clear(); + /// + /// sock.recv_vectored_with_ancillary(bufs, &mut ancillary)?; + /// for ancillary_result in ancillary.messages() { + /// if let IpAncillaryData::Ttl(ttl) = ancillary_result.unwrap() { + /// println!("receive packet with TTL: {}", ttl); + /// } + /// } + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn clear(&mut self) { + self.inner.clear(); + } +} diff --git a/library/std/src/os/unix/net/ancillary/mod.rs b/library/std/src/os/unix/net/ancillary/mod.rs new file mode 100644 index 0000000000000..44cc57f26dab2 --- /dev/null +++ b/library/std/src/os/unix/net/ancillary/mod.rs @@ -0,0 +1,9 @@ +mod inner; +#[cfg(any(doc, target_os = "android", target_os = "emscripten", target_os = "linux"))] +mod ip; +mod unix; + +pub use inner::Messages; +#[cfg(any(doc, target_os = "android", target_os = "emscripten", target_os = "linux"))] +pub use ip::{IpAncillary, IpAncillaryData}; +pub use unix::{SocketCred, UnixAncillary, UnixAncillaryData}; diff --git a/library/std/src/os/unix/net/ancillary.rs b/library/std/src/os/unix/net/ancillary/unix.rs similarity index 50% rename from library/std/src/os/unix/net/ancillary.rs rename to library/std/src/os/unix/net/ancillary/unix.rs index cd429d1426937..5c6440b2e86fa 100644 --- a/library/std/src/os/unix/net/ancillary.rs +++ b/library/std/src/os/unix/net/ancillary/unix.rs @@ -1,19 +1,19 @@ -use super::{sockaddr_un, SocketAddr}; -use crate::convert::TryFrom; -use crate::io::{self, IoSlice, IoSliceMut}; -use crate::marker::PhantomData; -use crate::mem::{size_of, zeroed}; -use crate::os::unix::io::RawFd; -use crate::path::Path; -use crate::ptr::{eq, read_unaligned}; -use crate::slice::from_raw_parts; -use crate::sys::net::Socket; +use super::{ + super::{sockaddr_un, SocketAddr}, + inner::{get_data_from_cmsghdr, Ancillary, AncillaryDataIter, AncillaryError, Messages}, +}; +use crate::{ + io::{self, IoSlice, IoSliceMut}, + mem::{size_of, zeroed}, + os::unix::io::RawFd, + path::Path, + sys::net::Socket, +}; // FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here? #[cfg(all(doc, not(target_os = "linux"), not(target_os = "android")))] #[allow(non_camel_case_types)] mod libc { - pub use libc::c_int; pub struct ucred; pub struct cmsghdr; pub type pid_t = i32; @@ -21,168 +21,53 @@ mod libc { pub type uid_t = u32; } -pub(super) fn recv_vectored_with_ancillary_from( - socket: &Socket, - bufs: &mut [IoSliceMut<'_>], - ancillary: &mut SocketAncillary<'_>, -) -> io::Result<(usize, bool, io::Result)> { - unsafe { - let mut msg_name: libc::sockaddr_un = zeroed(); - let mut msg: libc::msghdr = zeroed(); - msg.msg_name = &mut msg_name as *mut _ as *mut _; - msg.msg_namelen = size_of::() as libc::socklen_t; - msg.msg_iov = bufs.as_mut_ptr().cast(); - msg.msg_iovlen = bufs.len() as _; - msg.msg_controllen = ancillary.buffer.len() as _; - // macos requires that the control pointer is null when the len is 0. - if msg.msg_controllen > 0 { - msg.msg_control = ancillary.buffer.as_mut_ptr().cast(); - } - - let count = socket.recv_msg(&mut msg)?; - - ancillary.length = msg.msg_controllen as usize; - ancillary.truncated = msg.msg_flags & libc::MSG_CTRUNC == libc::MSG_CTRUNC; - - let truncated = msg.msg_flags & libc::MSG_TRUNC == libc::MSG_TRUNC; - let addr = SocketAddr::from_parts(msg_name, msg.msg_namelen); - - Ok((count, truncated, addr)) - } -} - -pub(super) fn send_vectored_with_ancillary_to( - socket: &Socket, - path: Option<&Path>, - bufs: &[IoSlice<'_>], - ancillary: &mut SocketAncillary<'_>, -) -> io::Result { - unsafe { - let (mut msg_name, msg_namelen) = - if let Some(path) = path { sockaddr_un(path)? } else { (zeroed(), 0) }; - - let mut msg: libc::msghdr = zeroed(); - msg.msg_name = &mut msg_name as *mut _ as *mut _; - msg.msg_namelen = msg_namelen; - msg.msg_iov = bufs.as_ptr() as *mut _; - msg.msg_iovlen = bufs.len() as _; - msg.msg_controllen = ancillary.length as _; - // macos requires that the control pointer is null when the len is 0. - if msg.msg_controllen > 0 { - msg.msg_control = ancillary.buffer.as_mut_ptr().cast(); - } - - ancillary.truncated = false; - - socket.send_msg(&mut msg) - } -} - -fn add_to_ancillary_data( - buffer: &mut [u8], - length: &mut usize, - source: &[T], - cmsg_level: libc::c_int, - cmsg_type: libc::c_int, -) -> bool { - let source_len = if let Some(source_len) = source.len().checked_mul(size_of::()) { - if let Ok(source_len) = u32::try_from(source_len) { - source_len - } else { - return false; - } - } else { - return false; - }; - - unsafe { - let additional_space = libc::CMSG_SPACE(source_len) as usize; - - let new_length = if let Some(new_length) = additional_space.checked_add(*length) { - new_length - } else { - return false; - }; - - if new_length > buffer.len() { - return false; - } - - buffer[*length..new_length].fill(0); - - *length = new_length; - - let mut msg: libc::msghdr = zeroed(); - msg.msg_control = buffer.as_mut_ptr().cast(); - msg.msg_controllen = *length as _; - - let mut cmsg = libc::CMSG_FIRSTHDR(&msg); - let mut previous_cmsg = cmsg; - while !cmsg.is_null() { - previous_cmsg = cmsg; - cmsg = libc::CMSG_NXTHDR(&msg, cmsg); - - // Most operating systems, but not Linux or emscripten, return the previous pointer - // when its length is zero. Therefore, check if the previous pointer is the same as - // the current one. - if eq(cmsg, previous_cmsg) { - break; - } - } - - if previous_cmsg.is_null() { - return false; +impl Socket { + pub(crate) fn recv_vectored_with_ancillary_from_unix( + &self, + bufs: &mut [IoSliceMut<'_>], + ancillary: &mut UnixAncillary<'_>, + ) -> io::Result<(usize, bool, io::Result)> { + unsafe { + let mut msg_name: libc::sockaddr_un = zeroed(); + let (count, truncated, msg_namelen) = self.recv_vectored_with_ancillary_from( + &mut msg_name as *mut _ as *mut libc::c_void, + size_of::() as libc::socklen_t, + bufs, + &mut ancillary.inner, + )?; + let addr = SocketAddr::from_parts(msg_name, msg_namelen); + + Ok((count, truncated, addr)) } - - (*previous_cmsg).cmsg_level = cmsg_level; - (*previous_cmsg).cmsg_type = cmsg_type; - (*previous_cmsg).cmsg_len = libc::CMSG_LEN(source_len) as _; - - let data = libc::CMSG_DATA(previous_cmsg).cast(); - - libc::memcpy(data, source.as_ptr().cast(), source_len as usize); - } - true -} - -struct AncillaryDataIter<'a, T> { - data: &'a [u8], - phantom: PhantomData, -} - -impl<'a, T> AncillaryDataIter<'a, T> { - /// Create `AncillaryDataIter` struct to iterate through the data unit in the control message. - /// - /// # Safety - /// - /// `data` must contain a valid control message. - unsafe fn new(data: &'a [u8]) -> AncillaryDataIter<'a, T> { - AncillaryDataIter { data, phantom: PhantomData } } -} -impl<'a, T> Iterator for AncillaryDataIter<'a, T> { - type Item = T; - - fn next(&mut self) -> Option { - if size_of::() <= self.data.len() { - unsafe { - let unit = read_unaligned(self.data.as_ptr().cast()); - self.data = &self.data[size_of::()..]; - Some(unit) - } - } else { - None + pub(crate) fn send_vectored_with_ancillary_to_unix( + &self, + path: Option<&Path>, + bufs: &[IoSlice<'_>], + ancillary: &mut UnixAncillary<'_>, + ) -> io::Result { + unsafe { + let (mut msg_name, msg_namelen) = + if let Some(path) = path { sockaddr_un(path)? } else { (zeroed(), 0) }; + self.send_vectored_with_ancillary_to( + &mut msg_name as *mut _ as *mut libc::c_void, + msg_namelen, + bufs, + &mut ancillary.inner, + ) } } } /// Unix credential. +#[doc(cfg(any(target_os = "android", target_os = "linux",)))] #[cfg(any(doc, target_os = "android", target_os = "linux",))] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] #[derive(Clone)] pub struct SocketCred(libc::ucred); +#[doc(cfg(any(target_os = "android", target_os = "linux",)))] #[cfg(any(doc, target_os = "android", target_os = "linux",))] impl SocketCred { /// Create a Unix credential struct. @@ -248,10 +133,12 @@ impl<'a> Iterator for ScmRights<'a> { /// This control message contains unix credentials. /// /// The level is equal to `SOL_SOCKET` and the type is equal to `SCM_CREDENTIALS` or `SCM_CREDS`. +#[doc(cfg(any(target_os = "android", target_os = "linux",)))] #[cfg(any(doc, target_os = "android", target_os = "linux",))] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub struct ScmCredentials<'a>(AncillaryDataIter<'a, libc::ucred>); +#[doc(cfg(any(target_os = "android", target_os = "linux",)))] #[cfg(any(doc, target_os = "android", target_os = "linux",))] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] impl<'a> Iterator for ScmCredentials<'a> { @@ -262,24 +149,18 @@ impl<'a> Iterator for ScmCredentials<'a> { } } -/// The error type which is returned from parsing the type a control message. -#[non_exhaustive] -#[derive(Debug)] -#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] -pub enum AncillaryError { - Unknown { cmsg_level: i32, cmsg_type: i32 }, -} - -/// This enum represent one control message of variable type. +/// This enum represent one control message of variable type for `SOL_SOCKET`. #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] -pub enum AncillaryData<'a> { +#[non_exhaustive] +pub enum UnixAncillaryData<'a> { ScmRights(ScmRights<'a>), + #[doc(cfg(any(target_os = "android", target_os = "linux",)))] #[cfg(any(doc, target_os = "android", target_os = "linux",))] ScmCredentials(ScmCredentials<'a>), } -impl<'a> AncillaryData<'a> { - /// Create a `AncillaryData::ScmRights` variant. +impl<'a> UnixAncillaryData<'a> { + /// Create a `UnixAncillaryData::ScmRights` variant. /// /// # Safety /// @@ -288,82 +169,47 @@ impl<'a> AncillaryData<'a> { unsafe fn as_rights(data: &'a [u8]) -> Self { let ancillary_data_iter = AncillaryDataIter::new(data); let scm_rights = ScmRights(ancillary_data_iter); - AncillaryData::ScmRights(scm_rights) + UnixAncillaryData::ScmRights(scm_rights) } - /// Create a `AncillaryData::ScmCredentials` variant. + /// Create a `UnixAncillaryData::ScmCredentials` variant. /// /// # Safety /// /// `data` must contain a valid control message and the control message must be type of /// `SOL_SOCKET` and level of `SCM_CREDENTIALS` or `SCM_CREDENTIALS`. - #[cfg(any(doc, target_os = "android", target_os = "linux",))] + #[cfg(any(target_os = "android", target_os = "linux",))] unsafe fn as_credentials(data: &'a [u8]) -> Self { let ancillary_data_iter = AncillaryDataIter::new(data); let scm_credentials = ScmCredentials(ancillary_data_iter); - AncillaryData::ScmCredentials(scm_credentials) + UnixAncillaryData::ScmCredentials(scm_credentials) } - fn try_from_cmsghdr(cmsg: &'a libc::cmsghdr) -> Result { - unsafe { - let cmsg_len_zero = libc::CMSG_LEN(0) as usize; - let data_len = (*cmsg).cmsg_len as usize - cmsg_len_zero; - let data = libc::CMSG_DATA(cmsg).cast(); - let data = from_raw_parts(data, data_len); - - match (*cmsg).cmsg_level { - libc::SOL_SOCKET => match (*cmsg).cmsg_type { - libc::SCM_RIGHTS => Ok(AncillaryData::as_rights(data)), - #[cfg(any(target_os = "android", target_os = "linux",))] - libc::SCM_CREDENTIALS => Ok(AncillaryData::as_credentials(data)), - cmsg_type => { - Err(AncillaryError::Unknown { cmsg_level: libc::SOL_SOCKET, cmsg_type }) - } - }, - cmsg_level => { - Err(AncillaryError::Unknown { cmsg_level, cmsg_type: (*cmsg).cmsg_type }) + unsafe fn try_from(cmsg: &'a libc::cmsghdr) -> Result { + let data = get_data_from_cmsghdr(cmsg); + + match (*cmsg).cmsg_level { + libc::SOL_SOCKET => match (*cmsg).cmsg_type { + libc::SCM_RIGHTS => Ok(UnixAncillaryData::as_rights(data)), + #[cfg(any(target_os = "android", target_os = "linux",))] + libc::SCM_CREDENTIALS => Ok(UnixAncillaryData::as_credentials(data)), + cmsg_type => { + Err(AncillaryError::Unknown { cmsg_level: libc::SOL_SOCKET, cmsg_type }) } - } + }, + cmsg_level => Err(AncillaryError::Unknown { cmsg_level, cmsg_type: (*cmsg).cmsg_type }), } } } -/// This struct is used to iterate through the control messages. -#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] -pub struct Messages<'a> { - buffer: &'a [u8], - current: Option<&'a libc::cmsghdr>, -} - #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] -impl<'a> Iterator for Messages<'a> { - type Item = Result, AncillaryError>; +impl<'a> Iterator for Messages<'a, UnixAncillaryData<'a>> { + type Item = Result, AncillaryError>; fn next(&mut self) -> Option { unsafe { - let mut msg: libc::msghdr = zeroed(); - msg.msg_control = self.buffer.as_ptr() as *mut _; - msg.msg_controllen = self.buffer.len() as _; - - let cmsg = if let Some(current) = self.current { - libc::CMSG_NXTHDR(&msg, current) - } else { - libc::CMSG_FIRSTHDR(&msg) - }; - - let cmsg = cmsg.as_ref()?; - - // Most operating systems, but not Linux or emscripten, return the previous pointer - // when its length is zero. Therefore, check if the previous pointer is the same as - // the current one. - if let Some(current) = self.current { - if eq(current, cmsg) { - return None; - } - } - - self.current = Some(cmsg); - let ancillary_result = AncillaryData::try_from_cmsghdr(cmsg); + let cmsg = self.next_cmsghdr()?; + let ancillary_result = UnixAncillaryData::try_from(cmsg); Some(ancillary_result) } } @@ -374,7 +220,7 @@ impl<'a> Iterator for Messages<'a> { /// # Example /// ```no_run /// #![feature(unix_socket_ancillary_data)] -/// use std::os::unix::net::{UnixStream, SocketAncillary, AncillaryData}; +/// use std::os::unix::net::{UnixStream, UnixAncillary, UnixAncillaryData}; /// use std::io::IoSliceMut; /// /// fn main() -> std::io::Result<()> { @@ -382,14 +228,14 @@ impl<'a> Iterator for Messages<'a> { /// /// let mut fds = [0; 8]; /// let mut ancillary_buffer = [0; 128]; -/// let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]); +/// let mut ancillary = UnixAncillary::new(&mut ancillary_buffer[..]); /// /// let mut buf = [1; 8]; /// let mut bufs = &mut [IoSliceMut::new(&mut buf[..])][..]; /// sock.recv_vectored_with_ancillary(bufs, &mut ancillary)?; /// /// for ancillary_result in ancillary.messages() { -/// if let AncillaryData::ScmRights(scm_rights) = ancillary_result.unwrap() { +/// if let UnixAncillaryData::ScmRights(scm_rights) = ancillary_result.unwrap() { /// for fd in scm_rights { /// println!("receive file descriptor: {}", fd); /// } @@ -400,13 +246,11 @@ impl<'a> Iterator for Messages<'a> { /// ``` #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] #[derive(Debug)] -pub struct SocketAncillary<'a> { - buffer: &'a mut [u8], - length: usize, - truncated: bool, +pub struct UnixAncillary<'a> { + pub(crate) inner: Ancillary<'a>, } -impl<'a> SocketAncillary<'a> { +impl<'a> UnixAncillary<'a> { /// Create an ancillary data with the given buffer. /// /// # Example @@ -414,37 +258,37 @@ impl<'a> SocketAncillary<'a> { /// ```no_run /// # #![allow(unused_mut)] /// #![feature(unix_socket_ancillary_data)] - /// use std::os::unix::net::SocketAncillary; + /// use std::os::unix::net::UnixAncillary; /// let mut ancillary_buffer = [0; 128]; - /// let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]); + /// let mut ancillary = UnixAncillary::new(&mut ancillary_buffer[..]); /// ``` #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn new(buffer: &'a mut [u8]) -> Self { - SocketAncillary { buffer, length: 0, truncated: false } + UnixAncillary { inner: Ancillary::new(buffer) } } /// Returns the capacity of the buffer. #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn capacity(&self) -> usize { - self.buffer.len() + self.inner.capacity() } /// Returns `true` if the ancillary data is empty. #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn is_empty(&self) -> bool { - self.length == 0 + self.inner.is_empty() } /// Returns the number of used bytes. #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn len(&self) -> usize { - self.length + self.inner.len() } /// Returns the iterator of the control messages. #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] - pub fn messages(&self) -> Messages<'_> { - Messages { buffer: &self.buffer[..self.length], current: None } + pub fn messages(&self) -> Messages<'_, UnixAncillaryData<'_>> { + self.inner.messages() } /// Is `true` if during a recv operation the ancillary was truncated. @@ -453,14 +297,14 @@ impl<'a> SocketAncillary<'a> { /// /// ```no_run /// #![feature(unix_socket_ancillary_data)] - /// use std::os::unix::net::{UnixStream, SocketAncillary}; + /// use std::os::unix::net::{UnixStream, UnixAncillary}; /// use std::io::IoSliceMut; /// /// fn main() -> std::io::Result<()> { /// let sock = UnixStream::connect("/tmp/sock")?; /// /// let mut ancillary_buffer = [0; 128]; - /// let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]); + /// let mut ancillary = UnixAncillary::new(&mut ancillary_buffer[..]); /// /// let mut buf = [1; 8]; /// let mut bufs = &mut [IoSliceMut::new(&mut buf[..])][..]; @@ -472,7 +316,7 @@ impl<'a> SocketAncillary<'a> { /// ``` #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn truncated(&self) -> bool { - self.truncated + self.inner.truncated() } /// Add file descriptors to the ancillary data. @@ -486,7 +330,7 @@ impl<'a> SocketAncillary<'a> { /// /// ```no_run /// #![feature(unix_socket_ancillary_data)] - /// use std::os::unix::net::{UnixStream, SocketAncillary}; + /// use std::os::unix::net::{UnixStream, UnixAncillary}; /// use std::os::unix::io::AsRawFd; /// use std::io::IoSlice; /// @@ -494,7 +338,7 @@ impl<'a> SocketAncillary<'a> { /// let sock = UnixStream::connect("/tmp/sock")?; /// /// let mut ancillary_buffer = [0; 128]; - /// let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]); + /// let mut ancillary = UnixAncillary::new(&mut ancillary_buffer[..]); /// ancillary.add_fds(&[sock.as_raw_fd()][..]); /// /// let mut buf = [1; 8]; @@ -505,14 +349,7 @@ impl<'a> SocketAncillary<'a> { /// ``` #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn add_fds(&mut self, fds: &[RawFd]) -> bool { - self.truncated = false; - add_to_ancillary_data( - &mut self.buffer, - &mut self.length, - fds, - libc::SOL_SOCKET, - libc::SCM_RIGHTS, - ) + self.inner.add_to_ancillary_data(fds, libc::SOL_SOCKET, libc::SCM_RIGHTS) } /// Add credentials to the ancillary data. @@ -522,17 +359,11 @@ impl<'a> SocketAncillary<'a> { /// Technically, that means this operation adds a control message with the level `SOL_SOCKET` /// and type `SCM_CREDENTIALS` or `SCM_CREDS`. /// + #[doc(cfg(any(target_os = "android", target_os = "linux",)))] #[cfg(any(doc, target_os = "android", target_os = "linux",))] #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn add_creds(&mut self, creds: &[SocketCred]) -> bool { - self.truncated = false; - add_to_ancillary_data( - &mut self.buffer, - &mut self.length, - creds, - libc::SOL_SOCKET, - libc::SCM_CREDENTIALS, - ) + self.inner.add_to_ancillary_data(creds, libc::SOL_SOCKET, libc::SCM_CREDENTIALS) } /// Clears the ancillary data, removing all values. @@ -541,7 +372,7 @@ impl<'a> SocketAncillary<'a> { /// /// ```no_run /// #![feature(unix_socket_ancillary_data)] - /// use std::os::unix::net::{UnixStream, SocketAncillary, AncillaryData}; + /// use std::os::unix::net::{UnixStream, UnixAncillary, UnixAncillaryData}; /// use std::io::IoSliceMut; /// /// fn main() -> std::io::Result<()> { @@ -550,14 +381,14 @@ impl<'a> SocketAncillary<'a> { /// let mut fds1 = [0; 8]; /// let mut fds2 = [0; 8]; /// let mut ancillary_buffer = [0; 128]; - /// let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]); + /// let mut ancillary = UnixAncillary::new(&mut ancillary_buffer[..]); /// /// let mut buf = [1; 8]; /// let mut bufs = &mut [IoSliceMut::new(&mut buf[..])][..]; /// /// sock.recv_vectored_with_ancillary(bufs, &mut ancillary)?; /// for ancillary_result in ancillary.messages() { - /// if let AncillaryData::ScmRights(scm_rights) = ancillary_result.unwrap() { + /// if let UnixAncillaryData::ScmRights(scm_rights) = ancillary_result.unwrap() { /// for fd in scm_rights { /// println!("receive file descriptor: {}", fd); /// } @@ -568,7 +399,7 @@ impl<'a> SocketAncillary<'a> { /// /// sock.recv_vectored_with_ancillary(bufs, &mut ancillary)?; /// for ancillary_result in ancillary.messages() { - /// if let AncillaryData::ScmRights(scm_rights) = ancillary_result.unwrap() { + /// if let UnixAncillaryData::ScmRights(scm_rights) = ancillary_result.unwrap() { /// for fd in scm_rights { /// println!("receive file descriptor: {}", fd); /// } @@ -579,7 +410,6 @@ impl<'a> SocketAncillary<'a> { /// ``` #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] pub fn clear(&mut self) { - self.length = 0; - self.truncated = false; + self.inner.clear(); } } diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index 9e39f70f68e69..5bf957c70fad1 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -8,7 +8,7 @@ target_os = "netbsd", target_os = "openbsd", ))] -use super::{recv_vectored_with_ancillary_from, send_vectored_with_ancillary_to, SocketAncillary}; +use super::UnixAncillary; use super::{sockaddr_un, SocketAddr}; #[cfg(any( target_os = "android", @@ -335,7 +335,7 @@ impl UnixDatagram { /// /// ```no_run /// #![feature(unix_socket_ancillary_data)] - /// use std::os::unix::net::{UnixDatagram, SocketAncillary, AncillaryData}; + /// use std::os::unix::net::{UnixDatagram, UnixAncillary, UnixAncillaryData}; /// use std::io::IoSliceMut; /// /// fn main() -> std::io::Result<()> { @@ -350,11 +350,11 @@ impl UnixDatagram { /// ][..]; /// let mut fds = [0; 8]; /// let mut ancillary_buffer = [0; 128]; - /// let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]); + /// let mut ancillary = UnixAncillary::new(&mut ancillary_buffer[..]); /// let (size, _truncated, sender) = sock.recv_vectored_with_ancillary_from(bufs, &mut ancillary)?; /// println!("received {}", size); /// for ancillary_result in ancillary.messages() { - /// if let AncillaryData::ScmRights(scm_rights) = ancillary_result.unwrap() { + /// if let UnixAncillaryData::ScmRights(scm_rights) = ancillary_result.unwrap() { /// for fd in scm_rights { /// println!("receive file descriptor: {}", fd); /// } @@ -376,9 +376,10 @@ impl UnixDatagram { pub fn recv_vectored_with_ancillary_from( &self, bufs: &mut [IoSliceMut<'_>], - ancillary: &mut SocketAncillary<'_>, + ancillary: &mut UnixAncillary<'_>, ) -> io::Result<(usize, bool, SocketAddr)> { - let (count, truncated, addr) = recv_vectored_with_ancillary_from(&self.0, bufs, ancillary)?; + let (count, truncated, addr) = + self.0.recv_vectored_with_ancillary_from_unix(bufs, ancillary)?; let addr = addr?; Ok((count, truncated, addr)) @@ -392,7 +393,7 @@ impl UnixDatagram { /// /// ```no_run /// #![feature(unix_socket_ancillary_data)] - /// use std::os::unix::net::{UnixDatagram, SocketAncillary, AncillaryData}; + /// use std::os::unix::net::{UnixDatagram, UnixAncillary, UnixAncillaryData}; /// use std::io::IoSliceMut; /// /// fn main() -> std::io::Result<()> { @@ -407,11 +408,11 @@ impl UnixDatagram { /// ][..]; /// let mut fds = [0; 8]; /// let mut ancillary_buffer = [0; 128]; - /// let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]); + /// let mut ancillary = UnixAncillary::new(&mut ancillary_buffer[..]); /// let (size, _truncated) = sock.recv_vectored_with_ancillary(bufs, &mut ancillary)?; /// println!("received {}", size); /// for ancillary_result in ancillary.messages() { - /// if let AncillaryData::ScmRights(scm_rights) = ancillary_result.unwrap() { + /// if let UnixAncillaryData::ScmRights(scm_rights) = ancillary_result.unwrap() { /// for fd in scm_rights { /// println!("receive file descriptor: {}", fd); /// } @@ -433,9 +434,10 @@ impl UnixDatagram { pub fn recv_vectored_with_ancillary( &self, bufs: &mut [IoSliceMut<'_>], - ancillary: &mut SocketAncillary<'_>, + ancillary: &mut UnixAncillary<'_>, ) -> io::Result<(usize, bool)> { - let (count, truncated, addr) = recv_vectored_with_ancillary_from(&self.0, bufs, ancillary)?; + let (count, truncated, addr) = + self.0.recv_vectored_with_ancillary_from_unix(bufs, ancillary)?; addr?; Ok((count, truncated)) @@ -505,7 +507,7 @@ impl UnixDatagram { /// /// ```no_run /// #![feature(unix_socket_ancillary_data)] - /// use std::os::unix::net::{UnixDatagram, SocketAncillary}; + /// use std::os::unix::net::{UnixDatagram, UnixAncillary}; /// use std::io::IoSlice; /// /// fn main() -> std::io::Result<()> { @@ -520,7 +522,7 @@ impl UnixDatagram { /// ][..]; /// let fds = [0, 1, 2]; /// let mut ancillary_buffer = [0; 128]; - /// let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]); + /// let mut ancillary = UnixAncillary::new(&mut ancillary_buffer[..]); /// ancillary.add_fds(&fds[..]); /// sock.send_vectored_with_ancillary_to(bufs, &mut ancillary, "/some/sock") /// .expect("send_vectored_with_ancillary_to function failed"); @@ -540,10 +542,10 @@ impl UnixDatagram { pub fn send_vectored_with_ancillary_to>( &self, bufs: &[IoSlice<'_>], - ancillary: &mut SocketAncillary<'_>, + ancillary: &mut UnixAncillary<'_>, path: P, ) -> io::Result { - send_vectored_with_ancillary_to(&self.0, Some(path.as_ref()), bufs, ancillary) + self.0.send_vectored_with_ancillary_to_unix(Some(path.as_ref()), bufs, ancillary) } /// Sends data and ancillary data on the socket. @@ -554,7 +556,7 @@ impl UnixDatagram { /// /// ```no_run /// #![feature(unix_socket_ancillary_data)] - /// use std::os::unix::net::{UnixDatagram, SocketAncillary}; + /// use std::os::unix::net::{UnixDatagram, UnixAncillary}; /// use std::io::IoSlice; /// /// fn main() -> std::io::Result<()> { @@ -569,7 +571,7 @@ impl UnixDatagram { /// ][..]; /// let fds = [0, 1, 2]; /// let mut ancillary_buffer = [0; 128]; - /// let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]); + /// let mut ancillary = UnixAncillary::new(&mut ancillary_buffer[..]); /// ancillary.add_fds(&fds[..]); /// sock.send_vectored_with_ancillary(bufs, &mut ancillary) /// .expect("send_vectored_with_ancillary function failed"); @@ -589,9 +591,9 @@ impl UnixDatagram { pub fn send_vectored_with_ancillary( &self, bufs: &[IoSlice<'_>], - ancillary: &mut SocketAncillary<'_>, + ancillary: &mut UnixAncillary<'_>, ) -> io::Result { - send_vectored_with_ancillary_to(&self.0, None, bufs, ancillary) + self.0.send_vectored_with_ancillary_to_unix(None, bufs, ancillary) } /// Sets the read timeout for the socket. @@ -742,7 +744,7 @@ impl UnixDatagram { self.0.set_nonblocking(nonblocking) } - /// Moves the socket to pass unix credentials as control message in [`SocketAncillary`]. + /// Moves the socket to pass unix credentials as control message in [`UnixAncillary`]. /// /// Set the socket option `SO_PASSCRED`. /// @@ -765,7 +767,7 @@ impl UnixDatagram { self.0.set_passcred(passcred) } - /// Get the current value of the socket for passing unix credentials in [`SocketAncillary`]. + /// Get the current value of the socket for passing unix credentials in [`UnixAncillary`]. /// This value can be change by [`set_passcred`]. /// /// Get the socket option `SO_PASSCRED`. diff --git a/library/std/src/os/unix/net/mod.rs b/library/std/src/os/unix/net/mod.rs index 3088ffb5e5c01..6f4a3cfdbd272 100644 --- a/library/std/src/os/unix/net/mod.rs +++ b/library/std/src/os/unix/net/mod.rs @@ -29,6 +29,8 @@ mod raw_fd; mod stream; #[cfg(all(test, not(target_os = "emscripten")))] mod tests; +#[cfg(any(doc, target_os = "android", target_os = "emscripten", target_os = "linux",))] +mod udp; #[stable(feature = "unix_socket", since = "1.10.0")] pub use self::addr::*; diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index fba084375e5f8..0b0f3f6ae7d7a 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -8,7 +8,7 @@ target_os = "netbsd", target_os = "openbsd", ))] -use super::{recv_vectored_with_ancillary_from, send_vectored_with_ancillary_to, SocketAncillary}; +use super::UnixAncillary; use super::{sockaddr_un, SocketAddr}; use crate::fmt; use crate::io::{self, Initializer, IoSlice, IoSliceMut}; @@ -361,7 +361,7 @@ impl UnixStream { self.0.set_nonblocking(nonblocking) } - /// Moves the socket to pass unix credentials as control message in [`SocketAncillary`]. + /// Moves the socket to pass unix credentials as control message in [`UnixAncillary`]. /// /// Set the socket option `SO_PASSCRED`. /// @@ -384,7 +384,7 @@ impl UnixStream { self.0.set_passcred(passcred) } - /// Get the current value of the socket for passing unix credentials in [`SocketAncillary`]. + /// Get the current value of the socket for passing unix credentials in [`UnixAncillary`]. /// This value can be change by [`set_passcred`]. /// /// Get the socket option `SO_PASSCRED`. @@ -476,7 +476,7 @@ impl UnixStream { /// /// ```no_run /// #![feature(unix_socket_ancillary_data)] - /// use std::os::unix::net::{UnixStream, SocketAncillary, AncillaryData}; + /// use std::os::unix::net::{UnixStream, UnixAncillary, UnixAncillaryData}; /// use std::io::IoSliceMut; /// /// fn main() -> std::io::Result<()> { @@ -491,11 +491,11 @@ impl UnixStream { /// ][..]; /// let mut fds = [0; 8]; /// let mut ancillary_buffer = [0; 128]; - /// let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]); + /// let mut ancillary = UnixAncillary::new(&mut ancillary_buffer[..]); /// let size = socket.recv_vectored_with_ancillary(bufs, &mut ancillary)?; /// println!("received {}", size); /// for ancillary_result in ancillary.messages() { - /// if let AncillaryData::ScmRights(scm_rights) = ancillary_result.unwrap() { + /// if let UnixAncillaryData::ScmRights(scm_rights) = ancillary_result.unwrap() { /// for fd in scm_rights { /// println!("receive file descriptor: {}", fd); /// } @@ -517,9 +517,9 @@ impl UnixStream { pub fn recv_vectored_with_ancillary( &self, bufs: &mut [IoSliceMut<'_>], - ancillary: &mut SocketAncillary<'_>, + ancillary: &mut UnixAncillary<'_>, ) -> io::Result { - let (count, _, _) = recv_vectored_with_ancillary_from(&self.0, bufs, ancillary)?; + let (count, _, _) = self.0.recv_vectored_with_ancillary_from_unix(bufs, ancillary)?; Ok(count) } @@ -532,7 +532,7 @@ impl UnixStream { /// /// ```no_run /// #![feature(unix_socket_ancillary_data)] - /// use std::os::unix::net::{UnixStream, SocketAncillary}; + /// use std::os::unix::net::{UnixStream, UnixAncillary}; /// use std::io::IoSlice; /// /// fn main() -> std::io::Result<()> { @@ -547,7 +547,7 @@ impl UnixStream { /// ][..]; /// let fds = [0, 1, 2]; /// let mut ancillary_buffer = [0; 128]; - /// let mut ancillary = SocketAncillary::new(&mut ancillary_buffer[..]); + /// let mut ancillary = UnixAncillary::new(&mut ancillary_buffer[..]); /// ancillary.add_fds(&fds[..]); /// socket.send_vectored_with_ancillary(bufs, &mut ancillary) /// .expect("send_vectored_with_ancillary function failed"); @@ -567,9 +567,9 @@ impl UnixStream { pub fn send_vectored_with_ancillary( &self, bufs: &[IoSlice<'_>], - ancillary: &mut SocketAncillary<'_>, + ancillary: &mut UnixAncillary<'_>, ) -> io::Result { - send_vectored_with_ancillary_to(&self.0, None, bufs, ancillary) + self.0.send_vectored_with_ancillary_to_unix(None, bufs, ancillary) } } diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index bd9b6dd727b96..5484f45f95601 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -1,6 +1,9 @@ use super::*; use crate::io::prelude::*; use crate::io::{self, ErrorKind, IoSlice, IoSliceMut}; +use crate::sys_common::io::test::tmpdir; +use crate::thread; +use crate::time::Duration; #[cfg(any( target_os = "android", target_os = "dragonfly", @@ -10,20 +13,7 @@ use crate::io::{self, ErrorKind, IoSlice, IoSliceMut}; target_os = "netbsd", target_os = "openbsd", ))] -use crate::iter::FromIterator; -#[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "emscripten", - target_os = "freebsd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", -))] -use crate::os::unix::io::AsRawFd; -use crate::sys_common::io::test::tmpdir; -use crate::thread; -use crate::time::Duration; +use crate::{iter::FromIterator, os::unix::io::AsRawFd}; macro_rules! or_panic { ($e:expr) => { @@ -489,7 +479,7 @@ fn test_send_vectored_fds_unix_stream() { let bufs_send = &[IoSlice::new(&buf1[..])][..]; let mut ancillary1_buffer = [0; 128]; - let mut ancillary1 = SocketAncillary::new(&mut ancillary1_buffer[..]); + let mut ancillary1 = UnixAncillary::new(&mut ancillary1_buffer[..]); assert!(ancillary1.add_fds(&[s1.as_raw_fd()][..])); let usize = or_panic!(s1.send_vectored_with_ancillary(&bufs_send, &mut ancillary1)); @@ -499,7 +489,7 @@ fn test_send_vectored_fds_unix_stream() { let mut bufs_recv = &mut [IoSliceMut::new(&mut buf2[..])][..]; let mut ancillary2_buffer = [0; 128]; - let mut ancillary2 = SocketAncillary::new(&mut ancillary2_buffer[..]); + let mut ancillary2 = UnixAncillary::new(&mut ancillary2_buffer[..]); let usize = or_panic!(s2.recv_vectored_with_ancillary(&mut bufs_recv, &mut ancillary2)); assert_eq!(usize, 8); @@ -507,7 +497,7 @@ fn test_send_vectored_fds_unix_stream() { let mut ancillary_data_vec = Vec::from_iter(ancillary2.messages()); assert_eq!(ancillary_data_vec.len(), 1); - if let AncillaryData::ScmRights(scm_rights) = ancillary_data_vec.pop().unwrap().unwrap() { + if let UnixAncillaryData::ScmRights(scm_rights) = ancillary_data_vec.pop().unwrap().unwrap() { let fd_vec = Vec::from_iter(scm_rights); assert_eq!(fd_vec.len(), 1); unsafe { @@ -546,7 +536,7 @@ fn test_send_vectored_with_ancillary_to_unix_datagram() { let bufs_send = &[IoSlice::new(&buf1[..])][..]; let mut ancillary1_buffer = [0; 128]; - let mut ancillary1 = SocketAncillary::new(&mut ancillary1_buffer[..]); + let mut ancillary1 = UnixAncillary::new(&mut ancillary1_buffer[..]); let mut cred1 = SocketCred::new(); cred1.set_pid(getpid()); cred1.set_uid(getuid()); @@ -561,7 +551,7 @@ fn test_send_vectored_with_ancillary_to_unix_datagram() { let mut bufs_recv = &mut [IoSliceMut::new(&mut buf2[..])][..]; let mut ancillary2_buffer = [0; 128]; - let mut ancillary2 = SocketAncillary::new(&mut ancillary2_buffer[..]); + let mut ancillary2 = UnixAncillary::new(&mut ancillary2_buffer[..]); let (usize, truncated, _addr) = or_panic!(bsock2.recv_vectored_with_ancillary_from(&mut bufs_recv, &mut ancillary2)); @@ -572,7 +562,7 @@ fn test_send_vectored_with_ancillary_to_unix_datagram() { let mut ancillary_data_vec = Vec::from_iter(ancillary2.messages()); assert_eq!(ancillary_data_vec.len(), 1); - if let AncillaryData::ScmCredentials(scm_credentials) = + if let UnixAncillaryData::ScmCredentials(scm_credentials) = ancillary_data_vec.pop().unwrap().unwrap() { let cred_vec = Vec::from_iter(scm_credentials); @@ -607,7 +597,7 @@ fn test_send_vectored_with_ancillary_unix_datagram() { let bufs_send = &[IoSlice::new(&buf1[..])][..]; let mut ancillary1_buffer = [0; 128]; - let mut ancillary1 = SocketAncillary::new(&mut ancillary1_buffer[..]); + let mut ancillary1 = UnixAncillary::new(&mut ancillary1_buffer[..]); assert!(ancillary1.add_fds(&[bsock1.as_raw_fd()][..])); or_panic!(bsock1.connect(&path2)); @@ -618,7 +608,7 @@ fn test_send_vectored_with_ancillary_unix_datagram() { let mut bufs_recv = &mut [IoSliceMut::new(&mut buf2[..])][..]; let mut ancillary2_buffer = [0; 128]; - let mut ancillary2 = SocketAncillary::new(&mut ancillary2_buffer[..]); + let mut ancillary2 = UnixAncillary::new(&mut ancillary2_buffer[..]); let (usize, truncated) = or_panic!(bsock2.recv_vectored_with_ancillary(&mut bufs_recv, &mut ancillary2)); @@ -628,7 +618,7 @@ fn test_send_vectored_with_ancillary_unix_datagram() { let mut ancillary_data_vec = Vec::from_iter(ancillary2.messages()); assert_eq!(ancillary_data_vec.len(), 1); - if let AncillaryData::ScmRights(scm_rights) = ancillary_data_vec.pop().unwrap().unwrap() { + if let UnixAncillaryData::ScmRights(scm_rights) = ancillary_data_vec.pop().unwrap().unwrap() { let fd_vec = Vec::from_iter(scm_rights); assert_eq!(fd_vec.len(), 1); unsafe { @@ -638,3 +628,51 @@ fn test_send_vectored_with_ancillary_unix_datagram() { unreachable!("must be ScmRights"); } } + +#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] +#[test] +fn test_send_vectored_with_ancillary_to_udp_socket() { + use super::{IpAncillary, IpAncillaryData}; + use crate::{ + io::{IoSlice, IoSliceMut}, + iter::FromIterator, + net::UdpSocket, + }; + + let socket1 = or_panic!(UdpSocket::bind("127.0.0.1:0")); + let socket2 = or_panic!(UdpSocket::bind("127.0.0.1:0")); + + let addr1 = or_panic!(socket1.local_addr()); + let addr2 = or_panic!(socket2.local_addr()); + + or_panic!(socket2.set_recvttl(true)); + + let buf1 = [1; 8]; + let bufs_send = &[IoSlice::new(&buf1[..])][..]; + + let mut ancillary1_buffer = [0; 64]; + let mut ancillary1 = IpAncillary::new(&mut ancillary1_buffer[..]); + assert!(ancillary1.add_ttl(20)); + + let usize = + or_panic!(socket1.send_vectored_with_ancillary_to(&bufs_send, &mut ancillary1, &addr2)); + assert_eq!(usize, 8); + + let mut buf2 = [0; 8]; + let mut bufs_recv = &mut [IoSliceMut::new(&mut buf2[..])][..]; + + let mut ancillary2_buffer = [0; 64]; + let mut ancillary2 = IpAncillary::new(&mut ancillary2_buffer[..]); + + let (usize, truncated, addr) = + or_panic!(socket2.recv_vectored_with_ancillary_from(&mut bufs_recv, &mut ancillary2)); + assert_eq!(usize, 8); + assert_eq!(truncated, false); + assert_eq!(addr1, addr); + assert_eq!(buf1, buf2); + + let mut ancillary_data_vec = Vec::from_iter(ancillary2.messages()); + assert_eq!(ancillary_data_vec.len(), 1); + let IpAncillaryData::Ttl(ttl) = ancillary_data_vec.pop().unwrap().unwrap(); + assert_eq!(ttl, 20); +} diff --git a/library/std/src/os/unix/net/udp.rs b/library/std/src/os/unix/net/udp.rs new file mode 100644 index 0000000000000..9e7c81cfae799 --- /dev/null +++ b/library/std/src/os/unix/net/udp.rs @@ -0,0 +1,216 @@ +use super::IpAncillary; +use crate::{ + io::{self, IoSlice, IoSliceMut}, + net::{SocketAddr, ToSocketAddrs, UdpSocket}, + sys_common::AsInner, +}; + +impl UdpSocket { + /// Sets the value of the `IP_RECVTTL` option for this socket. + /// + /// If enabled, received packets will come with ancillary data ([`IpAncillary`]) providing + /// the time-to-live (TTL) value of the packet. + /// + /// # Examples + /// + ///```no_run + /// #![feature(unix_socket_ancillary_data)] + /// use std::net::UdpSocket; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_recvttl(true).expect("set_recvttl function failed"); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn set_recvttl(&self, recvttl: bool) -> io::Result<()> { + self.as_inner().set_recvttl(recvttl) + } + + /// Get the current value of the socket for receiving TTL in [`IpAncillary`]. + /// This value can be change by [`set_recvttl`]. + /// + /// Get the socket option `IP_RECVTTL`. + /// + /// [`set_recvttl`]: UdpSocket::set_recvttl + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn recvttl(&self) -> io::Result { + self.as_inner().recvttl() + } + + /// Receives data and ancillary data from socket. + /// + /// On success, returns the number of bytes read. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_ancillary_data)] + /// use std::io::IoSliceMut; + /// use std::net::UdpSocket; + /// use std::os::unix::net::{IpAncillary, IpAncillaryData}; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_recvttl(true).expect("set_recvttl function failed"); + /// let mut buf1 = [1; 8]; + /// let mut buf2 = [2; 16]; + /// let mut buf3 = [3; 8]; + /// let mut bufs = &mut [ + /// IoSliceMut::new(&mut buf1), + /// IoSliceMut::new(&mut buf2), + /// IoSliceMut::new(&mut buf3), + /// ][..]; + /// let mut ancillary_buffer = [0; 128]; + /// let mut ancillary = IpAncillary::new(&mut ancillary_buffer[..]); + /// let (size, truncated) = socket.recv_vectored_with_ancillary(bufs, &mut ancillary)?; + /// println!("received {} truncated {}", size, truncated); + /// for ancillary_result in ancillary.messages() { + /// if let IpAncillaryData::Ttl(ttl) = ancillary_result.unwrap() { + /// println!("UDP packet has a TTL of {}", ttl); + /// } + /// } + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn recv_vectored_with_ancillary( + &self, + bufs: &mut [IoSliceMut<'_>], + ancillary: &mut IpAncillary<'_>, + ) -> io::Result<(usize, bool)> { + self.as_inner().recv_vectored_with_ancillary(bufs, ancillary) + } + + /// Receives data and ancillary data from socket. + /// + /// On success, returns the number of bytes read, if the data was truncated and the address + /// from whence the msg came. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_ancillary_data)] + /// use std::io::IoSliceMut; + /// use std::net::UdpSocket; + /// use std::os::unix::net::{IpAncillary, IpAncillaryData}; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_recvttl(true).expect("set_recvttl function failed"); + /// let mut buf1 = [1; 8]; + /// let mut buf2 = [2; 16]; + /// let mut buf3 = [3; 8]; + /// let mut bufs = &mut [ + /// IoSliceMut::new(&mut buf1), + /// IoSliceMut::new(&mut buf2), + /// IoSliceMut::new(&mut buf3), + /// ][..]; + /// let mut ancillary_buffer = [0; 128]; + /// let mut ancillary = IpAncillary::new(&mut ancillary_buffer[..]); + /// let (size, truncated, addr) = + /// socket.recv_vectored_with_ancillary_from(bufs, &mut ancillary)?; + /// println!("received {} truncated {} from {}", size, truncated, addr); + /// for ancillary_result in ancillary.messages() { + /// if let IpAncillaryData::Ttl(ttl) = ancillary_result.unwrap() { + /// println!("UDP packet has a TTL of {}", ttl); + /// } + /// } + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn recv_vectored_with_ancillary_from( + &self, + bufs: &mut [IoSliceMut<'_>], + ancillary: &mut IpAncillary<'_>, + ) -> io::Result<(usize, bool, SocketAddr)> { + self.as_inner().recv_vectored_with_ancillary_from(bufs, ancillary) + } + + /// Sends data and ancillary data on the socket. + /// + /// On success, returns the number of bytes written. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_ancillary_data)] + /// use std::io::IoSlice; + /// use std::net::UdpSocket; + /// use std::os::unix::net::IpAncillary; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.connect("127.0.0.1:41203").expect("couldn't connect to address"); + /// let buf1 = [1; 8]; + /// let buf2 = [2; 16]; + /// let buf3 = [3; 8]; + /// let bufs = &[ + /// IoSlice::new(&buf1), + /// IoSlice::new(&buf2), + /// IoSlice::new(&buf3), + /// ][..]; + /// let mut ancillary_buffer = [0; 128]; + /// let mut ancillary = IpAncillary::new(&mut ancillary_buffer[..]); + /// ancillary.add_ttl(20); + /// socket.send_vectored_with_ancillary(bufs, &mut ancillary) + /// .expect("send_vectored_with_ancillary function failed"); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn send_vectored_with_ancillary( + &self, + bufs: &[IoSlice<'_>], + ancillary: &mut IpAncillary<'_>, + ) -> io::Result { + self.as_inner().send_vectored_with_ancillary(bufs, ancillary) + } + + /// Sends data and ancillary data on the socket to the specified address. + /// + /// On success, returns the number of bytes written. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_ancillary_data)] + /// use std::io::IoSlice; + /// use std::net::UdpSocket; + /// use std::os::unix::net::IpAncillary; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// let buf1 = [1; 8]; + /// let buf2 = [2; 16]; + /// let buf3 = [3; 8]; + /// let bufs = &[ + /// IoSlice::new(&buf1), + /// IoSlice::new(&buf2), + /// IoSlice::new(&buf3), + /// ][..]; + /// let mut ancillary_buffer = [0; 128]; + /// let mut ancillary = IpAncillary::new(&mut ancillary_buffer[..]); + /// ancillary.add_ttl(20); + /// socket.send_vectored_with_ancillary_to(bufs, &mut ancillary, "127.0.0.1:4242") + /// .expect("send_vectored_with_ancillary function failed"); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn send_vectored_with_ancillary_to( + &self, + bufs: &[IoSlice<'_>], + ancillary: &mut IpAncillary<'_>, + addr: A, + ) -> io::Result { + match addr.to_socket_addrs()?.next() { + Some(addr) => self.as_inner().send_vectored_with_ancillary_to(bufs, ancillary, &addr), + None => { + Err(io::Error::new(io::ErrorKind::InvalidInput, "no addresses to send data to")) + } + } + } +} diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index 3f614fde08aca..8e29112981401 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -393,6 +393,17 @@ impl Socket { Ok(passcred != 0) } + #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] + pub fn set_recvttl(&self, recvttl: bool) -> io::Result<()> { + setsockopt(self, libc::IPPROTO_IP, libc::IP_RECVTTL, recvttl as libc::c_int) + } + + #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] + pub fn recvttl(&self) -> io::Result { + let recvttl: libc::c_int = getsockopt(self, libc::IPPROTO_IP, libc::IP_RECVTTL)?; + Ok(recvttl != 0) + } + #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { let mut nonblocking = nonblocking as libc::c_int; diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index d5f29c4a43970..b7618fe4a3d93 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -8,6 +8,16 @@ use crate::fmt; use crate::io::{self, Error, ErrorKind, IoSlice, IoSliceMut}; use crate::mem; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +#[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", +))] +use crate::os::unix::net::IpAncillary; use crate::ptr; use crate::sys::net::netc as c; use crate::sys::net::{cvt, cvt_gai, cvt_r, init, wrlen_t, Socket}; @@ -497,6 +507,59 @@ impl UdpSocket { self.inner.recv_from(buf) } + #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] + pub fn set_recvttl(&self, recvttl: bool) -> io::Result<()> { + self.inner.set_recvttl(recvttl) + } + + #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] + pub fn recvttl(&self) -> io::Result { + self.inner.recvttl() + } + + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + ))] + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn recv_vectored_with_ancillary( + &self, + bufs: &mut [IoSliceMut<'_>], + ancillary: &mut IpAncillary<'_>, + ) -> io::Result<(usize, bool)> { + let (count, truncated, _) = + self.inner.recv_vectored_with_ancillary_from_udp(bufs, ancillary)?; + + Ok((count, truncated)) + } + + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + ))] + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn recv_vectored_with_ancillary_from( + &self, + bufs: &mut [IoSliceMut<'_>], + ancillary: &mut IpAncillary<'_>, + ) -> io::Result<(usize, bool, SocketAddr)> { + let (count, truncated, addr) = + self.inner.recv_vectored_with_ancillary_from_udp(bufs, ancillary)?; + let addr = addr?; + + Ok((count, truncated, addr)) + } + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { self.inner.peek_from(buf) } @@ -517,6 +580,43 @@ impl UdpSocket { Ok(ret as usize) } + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + ))] + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn send_vectored_with_ancillary( + &self, + bufs: &[IoSlice<'_>], + ancillary: &mut IpAncillary<'_>, + ) -> io::Result { + self.inner.send_vectored_with_ancillary_to_udp(None, bufs, ancillary) + } + + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + ))] + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn send_vectored_with_ancillary_to( + &self, + bufs: &[IoSlice<'_>], + ancillary: &mut IpAncillary<'_>, + dst: &SocketAddr, + ) -> io::Result { + self.inner.send_vectored_with_ancillary_to_udp(Some(dst), bufs, ancillary) + } + pub fn duplicate(&self) -> io::Result { self.inner.duplicate().map(|s| UdpSocket { inner: s }) } From c40097a4daa88b6dfc12c2807b58863fea43846a Mon Sep 17 00:00:00 2001 From: LinkTed Date: Sun, 25 Jul 2021 22:10:01 +0200 Subject: [PATCH 2/2] unix: Add HopLimit to IpAncillary Extend UdpSocket to send and receive hop-limit. --- library/std/src/os/unix/net/ancillary/ip.rs | 65 ++++++++++++++++++- library/std/src/os/unix/net/ancillary/unix.rs | 7 +- library/std/src/os/unix/net/tests.rs | 58 ++++++++++++++--- library/std/src/os/unix/net/udp.rs | 33 ++++++++++ library/std/src/sys/unix/net.rs | 12 ++++ library/std/src/sys_common/net.rs | 10 +++ 6 files changed, 168 insertions(+), 17 deletions(-) diff --git a/library/std/src/os/unix/net/ancillary/ip.rs b/library/std/src/os/unix/net/ancillary/ip.rs index b338b671eed22..948d20fc2c815 100644 --- a/library/std/src/os/unix/net/ancillary/ip.rs +++ b/library/std/src/os/unix/net/ancillary/ip.rs @@ -60,6 +60,7 @@ impl Socket { #[non_exhaustive] pub enum IpAncillaryData { Ttl(u8), + HopLimit(u8), } impl IpAncillaryData { @@ -84,6 +85,17 @@ impl IpAncillaryData { IpAncillaryData::Ttl(ttl) } + /// Create a `AncillaryData::HopLimit` variant. + /// + /// # Safety + /// + /// `data` must contain a valid control message and the control message must be type of + /// `IPPROTO_IPV6` and level of `IPV6_HOPLIMIT`. + unsafe fn as_hop_limit(data: &[u8]) -> Self { + let hop_limit = IpAncillaryData::as_u8(data); + IpAncillaryData::HopLimit(hop_limit) + } + unsafe fn try_from(cmsg: &libc::cmsghdr) -> Result { let data = get_data_from_cmsghdr(cmsg); @@ -94,6 +106,12 @@ impl IpAncillaryData { Err(AncillaryError::Unknown { cmsg_level: libc::IPPROTO_IP, cmsg_type }) } }, + libc::IPPROTO_IPV6 => match (*cmsg).cmsg_type { + libc::IPV6_HOPLIMIT => Ok(IpAncillaryData::as_hop_limit(data)), + cmsg_type => { + Err(AncillaryError::Unknown { cmsg_level: libc::IPPROTO_IPV6, cmsg_type }) + } + }, cmsg_level => Err(AncillaryError::Unknown { cmsg_level, cmsg_type: (*cmsg).cmsg_type }), } } @@ -146,7 +164,7 @@ pub struct IpAncillary<'a> { } impl<'a> IpAncillary<'a> { - /// Create an ancillary data with the given buffer. + /// Create ancillary data backed by the given buffer. /// /// # Example /// @@ -186,12 +204,53 @@ impl<'a> IpAncillary<'a> { self.inner.messages() } + /// Add hop-limit to the ancillary data. + /// + /// The function returns `true` if there was enough space in the buffer. + /// If there was not enough space then no hop-limit was appended. + /// This adds a control message with the level `IPPROTO_IPV6` and type `IPV6_HOPLIMIT`. + /// + /// # Example + /// ```no_run + /// #![feature(unix_socket_ancillary_data)] + /// use std::io::IoSlice; + /// use std::net::UdpSocket; + /// use std::os::unix::net::IpAncillary; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UdpSocket::bind("[::1]:34254")?; + /// sock.connect("[::1]:41203")?; + /// let buf1 = [1; 8]; + /// let buf2 = [2; 16]; + /// let buf3 = [3; 8]; + /// let bufs = &[ + /// IoSlice::new(&buf1), + /// IoSlice::new(&buf2), + /// IoSlice::new(&buf3), + /// ][..]; + /// let mut ancillary_buffer = [0; 128]; + /// let mut ancillary = IpAncillary::new(&mut ancillary_buffer[..]); + /// ancillary.add_hop_limit(20); + /// sock.send_vectored_with_ancillary(bufs, &mut ancillary) + /// .expect("send_vectored_with_ancillary function failed"); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn add_hop_limit(&mut self, hop_limit: u8) -> bool { + let hop_limit: libc::c_int = hop_limit as libc::c_int; + self.inner.add_to_ancillary_data( + from_ref(&hop_limit), + libc::IPPROTO_IPV6, + libc::IPV6_HOPLIMIT, + ) + } + /// Add TTL to the ancillary data. /// /// The function returns `true` if there was enough space in the buffer. /// If there was not enough space then no file descriptors was appended. - /// Technically, that means this operation adds a control message with the level `IPPROTO_IP` - /// and type `IP_TTL`. + /// This adds a control message with the level `IPPROTO_IP` and type `IP_TTL`. /// /// # Example /// ```no_run diff --git a/library/std/src/os/unix/net/ancillary/unix.rs b/library/std/src/os/unix/net/ancillary/unix.rs index 5c6440b2e86fa..1c7e46678b64e 100644 --- a/library/std/src/os/unix/net/ancillary/unix.rs +++ b/library/std/src/os/unix/net/ancillary/unix.rs @@ -323,8 +323,7 @@ impl<'a> UnixAncillary<'a> { /// /// The function returns `true` if there was enough space in the buffer. /// If there was not enough space then no file descriptors was appended. - /// Technically, that means this operation adds a control message with the level `SOL_SOCKET` - /// and type `SCM_RIGHTS`. + /// This adds a control message with the level `SOL_SOCKET` and type `SCM_RIGHTS`. /// /// # Example /// @@ -356,8 +355,8 @@ impl<'a> UnixAncillary<'a> { /// /// The function returns `true` if there was enough space in the buffer. /// If there was not enough space then no credentials was appended. - /// Technically, that means this operation adds a control message with the level `SOL_SOCKET` - /// and type `SCM_CREDENTIALS` or `SCM_CREDS`. + /// This adds a control message with the level `SOL_SOCKET` and type `SCM_CREDENTIALS` + /// or `SCM_CREDS`. /// #[doc(cfg(any(target_os = "android", target_os = "linux",)))] #[cfg(any(doc, target_os = "android", target_os = "linux",))] diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index 5484f45f95601..9b182cbe0105b 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -1,6 +1,8 @@ use super::*; use crate::io::prelude::*; use crate::io::{self, ErrorKind, IoSlice, IoSliceMut}; +#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] +use crate::net::UdpSocket; use crate::sys_common::io::test::tmpdir; use crate::thread; use crate::time::Duration; @@ -631,14 +633,7 @@ fn test_send_vectored_with_ancillary_unix_datagram() { #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] #[test] -fn test_send_vectored_with_ancillary_to_udp_socket() { - use super::{IpAncillary, IpAncillaryData}; - use crate::{ - io::{IoSlice, IoSliceMut}, - iter::FromIterator, - net::UdpSocket, - }; - +fn test_send_vectored_with_ancillary_to_udp_socket_ttl() { let socket1 = or_panic!(UdpSocket::bind("127.0.0.1:0")); let socket2 = or_panic!(UdpSocket::bind("127.0.0.1:0")); @@ -673,6 +668,49 @@ fn test_send_vectored_with_ancillary_to_udp_socket() { let mut ancillary_data_vec = Vec::from_iter(ancillary2.messages()); assert_eq!(ancillary_data_vec.len(), 1); - let IpAncillaryData::Ttl(ttl) = ancillary_data_vec.pop().unwrap().unwrap(); - assert_eq!(ttl, 20); + assert!( + matches!(ancillary_data_vec.pop().unwrap().unwrap(), IpAncillaryData::Ttl(ttl) if ttl == 20) + ); +} + +#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] +#[test] +fn test_send_vectored_with_ancillary_to_udp_socket_hop_limit() { + let socket1 = or_panic!(UdpSocket::bind("[::1]:0")); + let socket2 = or_panic!(UdpSocket::bind("[::1]:0")); + + let addr1 = or_panic!(socket1.local_addr()); + let addr2 = or_panic!(socket2.local_addr()); + + or_panic!(socket2.set_recvhoplimit(true)); + + let buf1 = [1; 8]; + let bufs_send = &[IoSlice::new(&buf1[..])][..]; + + let mut ancillary1_buffer = [0; 64]; + let mut ancillary1 = IpAncillary::new(&mut ancillary1_buffer[..]); + assert!(ancillary1.add_hop_limit(20)); + + let usize = + or_panic!(socket1.send_vectored_with_ancillary_to(&bufs_send, &mut ancillary1, &addr2)); + assert_eq!(usize, 8); + + let mut buf2 = [0; 8]; + let mut bufs_recv = &mut [IoSliceMut::new(&mut buf2[..])][..]; + + let mut ancillary2_buffer = [0; 64]; + let mut ancillary2 = IpAncillary::new(&mut ancillary2_buffer[..]); + + let (usize, truncated, addr) = + or_panic!(socket2.recv_vectored_with_ancillary_from(&mut bufs_recv, &mut ancillary2)); + assert_eq!(usize, 8); + assert_eq!(truncated, false); + assert_eq!(addr1, addr); + assert_eq!(buf1, buf2); + + let mut ancillary_data_vec = Vec::from_iter(ancillary2.messages()); + assert_eq!(ancillary_data_vec.len(), 1); + assert!( + matches!(ancillary_data_vec.pop().unwrap().unwrap(), IpAncillaryData::HopLimit(ttl) if ttl == 20) + ); } diff --git a/library/std/src/os/unix/net/udp.rs b/library/std/src/os/unix/net/udp.rs index 9e7c81cfae799..b97c4b541cea9 100644 --- a/library/std/src/os/unix/net/udp.rs +++ b/library/std/src/os/unix/net/udp.rs @@ -6,6 +6,39 @@ use crate::{ }; impl UdpSocket { + /// Sets the value of the `IPV6_RECVHOPLIMIT` option for this socket. + /// + /// If enabled, received packets will come with ancillary data ([`IpAncillary`]) providing + /// the hop-limit value of the packet. + /// + /// # Examples + /// + ///```no_run + /// #![feature(unix_socket_ancillary_data)] + /// use std::net::UdpSocket; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UdpSocket::bind("[::1]:34254")?; + /// socket.set_recvhoplimit(true)?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn set_recvhoplimit(&self, recvhoplimit: bool) -> io::Result<()> { + self.as_inner().set_recvhoplimit(recvhoplimit) + } + + /// Get the current value of the socket for receiving TTL in [`IpAncillary`]. + /// This value can be change by [`set_recvhoplimit`]. + /// + /// Get the socket option `IPV6_RECVHOPLIMIT`. + /// + /// [`set_recvhoplimit`]: UdpSocket::set_recvhoplimit + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn recvhoplimit(&self) -> io::Result { + self.as_inner().recvhoplimit() + } + /// Sets the value of the `IP_RECVTTL` option for this socket. /// /// If enabled, received packets will come with ancillary data ([`IpAncillary`]) providing diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index 8e29112981401..237119eab6acf 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -393,6 +393,18 @@ impl Socket { Ok(passcred != 0) } + #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] + pub fn set_recvhoplimit(&self, recvhoplimit: bool) -> io::Result<()> { + setsockopt(self, libc::IPPROTO_IPV6, libc::IPV6_RECVHOPLIMIT, recvhoplimit as libc::c_int) + } + + #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] + pub fn recvhoplimit(&self) -> io::Result { + let recvhoplimit: libc::c_int = + getsockopt(self, libc::IPPROTO_IPV6, libc::IPV6_RECVHOPLIMIT)?; + Ok(recvhoplimit != 0) + } + #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] pub fn set_recvttl(&self, recvttl: bool) -> io::Result<()> { setsockopt(self, libc::IPPROTO_IP, libc::IP_RECVTTL, recvttl as libc::c_int) diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index b7618fe4a3d93..eb51dcaca5856 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -507,6 +507,16 @@ impl UdpSocket { self.inner.recv_from(buf) } + #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] + pub fn set_recvhoplimit(&self, recvhoplimit: bool) -> io::Result<()> { + self.inner.set_recvhoplimit(recvhoplimit) + } + + #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] + pub fn recvhoplimit(&self) -> io::Result { + self.inner.recvhoplimit() + } + #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] pub fn set_recvttl(&self, recvttl: bool) -> io::Result<()> { self.inner.set_recvttl(recvttl)