diff --git a/library/std/src/os/unix/net/ancillary.rs b/library/std/src/os/unix/net/ancillary.rs deleted file mode 100644 index cd429d1426937..0000000000000 --- a/library/std/src/os/unix/net/ancillary.rs +++ /dev/null @@ -1,585 +0,0 @@ -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; - -// 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; - pub type gid_t = u32; - 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; - } - - (*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 - } - } -} - -/// Unix credential. -#[cfg(any(doc, target_os = "android", target_os = "linux",))] -#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] -#[derive(Clone)] -pub struct SocketCred(libc::ucred); - -#[cfg(any(doc, target_os = "android", target_os = "linux",))] -impl SocketCred { - /// Create a Unix credential struct. - /// - /// PID, UID and GID is set to 0. - #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] - pub fn new() -> SocketCred { - SocketCred(libc::ucred { pid: 0, uid: 0, gid: 0 }) - } - - /// Set the PID. - #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] - pub fn set_pid(&mut self, pid: libc::pid_t) { - self.0.pid = pid; - } - - /// Get the current PID. - #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] - pub fn get_pid(&self) -> libc::pid_t { - self.0.pid - } - - /// Set the UID. - #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] - pub fn set_uid(&mut self, uid: libc::uid_t) { - self.0.uid = uid; - } - - /// Get the current UID. - #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] - pub fn get_uid(&self) -> libc::uid_t { - self.0.uid - } - - /// Set the GID. - #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] - pub fn set_gid(&mut self, gid: libc::gid_t) { - self.0.gid = gid; - } - - /// Get the current GID. - #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] - pub fn get_gid(&self) -> libc::gid_t { - self.0.gid - } -} - -/// This control message contains file descriptors. -/// -/// The level is equal to `SOL_SOCKET` and the type is equal to `SCM_RIGHTS`. -#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] -pub struct ScmRights<'a>(AncillaryDataIter<'a, RawFd>); - -#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] -impl<'a> Iterator for ScmRights<'a> { - type Item = RawFd; - - fn next(&mut self) -> Option { - self.0.next() - } -} - -/// This control message contains unix credentials. -/// -/// The level is equal to `SOL_SOCKET` and the type is equal to `SCM_CREDENTIALS` or `SCM_CREDS`. -#[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>); - -#[cfg(any(doc, target_os = "android", target_os = "linux",))] -#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] -impl<'a> Iterator for ScmCredentials<'a> { - type Item = SocketCred; - - fn next(&mut self) -> Option { - Some(SocketCred(self.0.next()?)) - } -} - -/// 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. -#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] -pub enum AncillaryData<'a> { - ScmRights(ScmRights<'a>), - #[cfg(any(doc, target_os = "android", target_os = "linux",))] - ScmCredentials(ScmCredentials<'a>), -} - -impl<'a> AncillaryData<'a> { - /// Create a `AncillaryData::ScmRights` variant. - /// - /// # Safety - /// - /// `data` must contain a valid control message and the control message must be type of - /// `SOL_SOCKET` and level of `SCM_RIGHTS`. - 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) - } - - /// Create a `AncillaryData::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",))] - 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) - } - - 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 }) - } - } - } - } -} - -/// 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>; - - 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); - Some(ancillary_result) - } - } -} - -/// A Unix socket Ancillary data struct. -/// -/// # Example -/// ```no_run -/// #![feature(unix_socket_ancillary_data)] -/// use std::os::unix::net::{UnixStream, SocketAncillary, AncillaryData}; -/// use std::io::IoSliceMut; -/// -/// fn main() -> std::io::Result<()> { -/// let sock = UnixStream::connect("/tmp/sock")?; -/// -/// let mut fds = [0; 8]; -/// let mut ancillary_buffer = [0; 128]; -/// let mut ancillary = SocketAncillary::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() { -/// for fd in scm_rights { -/// println!("receive file descriptor: {}", fd); -/// } -/// } -/// } -/// Ok(()) -/// } -/// ``` -#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] -#[derive(Debug)] -pub struct SocketAncillary<'a> { - buffer: &'a mut [u8], - length: usize, - truncated: bool, -} - -impl<'a> SocketAncillary<'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::SocketAncillary; - /// let mut ancillary_buffer = [0; 128]; - /// let mut ancillary = SocketAncillary::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 } - } - - /// Returns the capacity of the buffer. - #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] - pub fn capacity(&self) -> usize { - self.buffer.len() - } - - /// 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 - } - - /// Returns the number of used bytes. - #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] - pub fn len(&self) -> usize { - self.length - } - - /// 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 } - } - - /// Is `true` if during a recv operation the ancillary was truncated. - /// - /// # Example - /// - /// ```no_run - /// #![feature(unix_socket_ancillary_data)] - /// use std::os::unix::net::{UnixStream, SocketAncillary}; - /// 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 buf = [1; 8]; - /// let mut bufs = &mut [IoSliceMut::new(&mut buf[..])][..]; - /// sock.recv_vectored_with_ancillary(bufs, &mut ancillary)?; - /// - /// println!("Is truncated: {}", ancillary.truncated()); - /// Ok(()) - /// } - /// ``` - #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] - pub fn truncated(&self) -> bool { - self.truncated - } - - /// Add file descriptors 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 `SOL_SOCKET` - /// and type `SCM_RIGHTS`. - /// - /// # Example - /// - /// ```no_run - /// #![feature(unix_socket_ancillary_data)] - /// use std::os::unix::net::{UnixStream, SocketAncillary}; - /// use std::os::unix::io::AsRawFd; - /// use std::io::IoSlice; - /// - /// 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[..]); - /// ancillary.add_fds(&[sock.as_raw_fd()][..]); - /// - /// let mut buf = [1; 8]; - /// let mut bufs = &mut [IoSlice::new(&mut buf[..])][..]; - /// sock.send_vectored_with_ancillary(bufs, &mut ancillary)?; - /// Ok(()) - /// } - /// ``` - #[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, - ) - } - - /// Add credentials to the ancillary data. - /// - /// 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`. - /// - #[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, - ) - } - - /// Clears the ancillary data, removing all values. - /// - /// # Example - /// - /// ```no_run - /// #![feature(unix_socket_ancillary_data)] - /// use std::os::unix::net::{UnixStream, SocketAncillary, AncillaryData}; - /// use std::io::IoSliceMut; - /// - /// fn main() -> std::io::Result<()> { - /// let sock = UnixStream::connect("/tmp/sock")?; - /// - /// 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 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() { - /// for fd in scm_rights { - /// println!("receive file descriptor: {}", fd); - /// } - /// } - /// } - /// - /// ancillary.clear(); - /// - /// sock.recv_vectored_with_ancillary(bufs, &mut ancillary)?; - /// for ancillary_result in ancillary.messages() { - /// if let AncillaryData::ScmRights(scm_rights) = ancillary_result.unwrap() { - /// for fd in scm_rights { - /// println!("receive file descriptor: {}", fd); - /// } - /// } - /// } - /// Ok(()) - /// } - /// ``` - #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] - pub fn clear(&mut self) { - self.length = 0; - self.truncated = false; - } -} 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..948d20fc2c815 --- /dev/null +++ b/library/std/src/os/unix/net/ancillary/ip.rs @@ -0,0 +1,328 @@ +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), + HopLimit(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) + } + + /// 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); + + 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 }) + } + }, + 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 }), + } + } +} + +#[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 ancillary data backed by 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 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. + /// This 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/unix.rs b/library/std/src/os/unix/net/ancillary/unix.rs new file mode 100644 index 0000000000000..1c7e46678b64e --- /dev/null +++ b/library/std/src/os/unix/net/ancillary/unix.rs @@ -0,0 +1,414 @@ +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 struct ucred; + pub struct cmsghdr; + pub type pid_t = i32; + pub type gid_t = u32; + pub type uid_t = u32; +} + +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)) + } + } + + 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. + /// + /// PID, UID and GID is set to 0. + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn new() -> SocketCred { + SocketCred(libc::ucred { pid: 0, uid: 0, gid: 0 }) + } + + /// Set the PID. + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn set_pid(&mut self, pid: libc::pid_t) { + self.0.pid = pid; + } + + /// Get the current PID. + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn get_pid(&self) -> libc::pid_t { + self.0.pid + } + + /// Set the UID. + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn set_uid(&mut self, uid: libc::uid_t) { + self.0.uid = uid; + } + + /// Get the current UID. + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn get_uid(&self) -> libc::uid_t { + self.0.uid + } + + /// Set the GID. + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn set_gid(&mut self, gid: libc::gid_t) { + self.0.gid = gid; + } + + /// Get the current GID. + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn get_gid(&self) -> libc::gid_t { + self.0.gid + } +} + +/// This control message contains file descriptors. +/// +/// The level is equal to `SOL_SOCKET` and the type is equal to `SCM_RIGHTS`. +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +pub struct ScmRights<'a>(AncillaryDataIter<'a, RawFd>); + +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +impl<'a> Iterator for ScmRights<'a> { + type Item = RawFd; + + fn next(&mut self) -> Option { + self.0.next() + } +} + +/// 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> { + type Item = SocketCred; + + fn next(&mut self) -> Option { + Some(SocketCred(self.0.next()?)) + } +} + +/// This enum represent one control message of variable type for `SOL_SOCKET`. +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +#[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> UnixAncillaryData<'a> { + /// Create a `UnixAncillaryData::ScmRights` variant. + /// + /// # Safety + /// + /// `data` must contain a valid control message and the control message must be type of + /// `SOL_SOCKET` and level of `SCM_RIGHTS`. + unsafe fn as_rights(data: &'a [u8]) -> Self { + let ancillary_data_iter = AncillaryDataIter::new(data); + let scm_rights = ScmRights(ancillary_data_iter); + UnixAncillaryData::ScmRights(scm_rights) + } + + /// 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(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); + UnixAncillaryData::ScmCredentials(scm_credentials) + } + + 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 }), + } + } +} + +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +impl<'a> Iterator for Messages<'a, UnixAncillaryData<'a>> { + type Item = Result, AncillaryError>; + + fn next(&mut self) -> Option { + unsafe { + let cmsg = self.next_cmsghdr()?; + let ancillary_result = UnixAncillaryData::try_from(cmsg); + Some(ancillary_result) + } + } +} + +/// A Unix socket Ancillary data struct. +/// +/// # Example +/// ```no_run +/// #![feature(unix_socket_ancillary_data)] +/// use std::os::unix::net::{UnixStream, UnixAncillary, UnixAncillaryData}; +/// use std::io::IoSliceMut; +/// +/// fn main() -> std::io::Result<()> { +/// let sock = UnixStream::connect("/tmp/sock")?; +/// +/// let mut fds = [0; 8]; +/// let mut ancillary_buffer = [0; 128]; +/// 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 UnixAncillaryData::ScmRights(scm_rights) = ancillary_result.unwrap() { +/// for fd in scm_rights { +/// println!("receive file descriptor: {}", fd); +/// } +/// } +/// } +/// Ok(()) +/// } +/// ``` +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +#[derive(Debug)] +pub struct UnixAncillary<'a> { + pub(crate) inner: Ancillary<'a>, +} + +impl<'a> UnixAncillary<'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::UnixAncillary; + /// let mut ancillary_buffer = [0; 128]; + /// let mut ancillary = UnixAncillary::new(&mut ancillary_buffer[..]); + /// ``` + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn new(buffer: &'a mut [u8]) -> Self { + 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.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<'_, UnixAncillaryData<'_>> { + self.inner.messages() + } + + /// Is `true` if during a recv operation the ancillary was truncated. + /// + /// # Example + /// + /// ```no_run + /// #![feature(unix_socket_ancillary_data)] + /// 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 = 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)?; + /// + /// println!("Is truncated: {}", ancillary.truncated()); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn truncated(&self) -> bool { + self.inner.truncated() + } + + /// Add file descriptors 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. + /// This adds a control message with the level `SOL_SOCKET` and type `SCM_RIGHTS`. + /// + /// # Example + /// + /// ```no_run + /// #![feature(unix_socket_ancillary_data)] + /// use std::os::unix::net::{UnixStream, UnixAncillary}; + /// use std::os::unix::io::AsRawFd; + /// use std::io::IoSlice; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixStream::connect("/tmp/sock")?; + /// + /// let mut ancillary_buffer = [0; 128]; + /// let mut ancillary = UnixAncillary::new(&mut ancillary_buffer[..]); + /// ancillary.add_fds(&[sock.as_raw_fd()][..]); + /// + /// let mut buf = [1; 8]; + /// let mut bufs = &mut [IoSlice::new(&mut buf[..])][..]; + /// sock.send_vectored_with_ancillary(bufs, &mut ancillary)?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn add_fds(&mut self, fds: &[RawFd]) -> bool { + self.inner.add_to_ancillary_data(fds, libc::SOL_SOCKET, libc::SCM_RIGHTS) + } + + /// Add credentials to the ancillary data. + /// + /// The function returns `true` if there was enough space in the buffer. + /// If there was not enough space then no credentials was appended. + /// 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",))] + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + pub fn add_creds(&mut self, creds: &[SocketCred]) -> bool { + self.inner.add_to_ancillary_data(creds, libc::SOL_SOCKET, libc::SCM_CREDENTIALS) + } + + /// Clears the ancillary data, removing all values. + /// + /// # Example + /// + /// ```no_run + /// #![feature(unix_socket_ancillary_data)] + /// use std::os::unix::net::{UnixStream, UnixAncillary, UnixAncillaryData}; + /// use std::io::IoSliceMut; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixStream::connect("/tmp/sock")?; + /// + /// let mut fds1 = [0; 8]; + /// let mut fds2 = [0; 8]; + /// let mut ancillary_buffer = [0; 128]; + /// 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 UnixAncillaryData::ScmRights(scm_rights) = ancillary_result.unwrap() { + /// for fd in scm_rights { + /// println!("receive file descriptor: {}", fd); + /// } + /// } + /// } + /// + /// ancillary.clear(); + /// + /// sock.recv_vectored_with_ancillary(bufs, &mut ancillary)?; + /// for ancillary_result in ancillary.messages() { + /// if let UnixAncillaryData::ScmRights(scm_rights) = ancillary_result.unwrap() { + /// for fd in scm_rights { + /// println!("receive file descriptor: {}", fd); + /// } + /// } + /// } + /// 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/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..9b182cbe0105b 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -1,6 +1,11 @@ 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; #[cfg(any( target_os = "android", target_os = "dragonfly", @@ -10,20 +15,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 +481,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 +491,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 +499,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 +538,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 +553,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 +564,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 +599,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 +610,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 +620,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 +630,87 @@ 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_ttl() { + 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); + 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 new file mode 100644 index 0000000000000..b97c4b541cea9 --- /dev/null +++ b/library/std/src/os/unix/net/udp.rs @@ -0,0 +1,249 @@ +use super::IpAncillary; +use crate::{ + io::{self, IoSlice, IoSliceMut}, + net::{SocketAddr, ToSocketAddrs, UdpSocket}, + sys_common::AsInner, +}; + +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 + /// 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..237119eab6acf 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -393,6 +393,29 @@ 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) + } + + #[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..eb51dcaca5856 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,69 @@ 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) + } + + #[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 +590,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 }) }