From b9eb55c33ab7c363e71a553a632ded92a18a0f0d Mon Sep 17 00:00:00 2001 From: John Millikin Date: Wed, 25 Oct 2023 17:01:21 +0900 Subject: [PATCH] Convert `Unix{Datagram,Stream}::{set_}passcred()` to per-OS traits These methods are the pre-stabilized API for obtaining peer credentials from an `AF_UNIX` socket, part of the `unix_socket_ancillary_data` feature. Their current behavior is to get/set one of the `SO_PASSCRED` (Linux), `LOCAL_CREDS_PERSISTENT` (FreeBSD), or `LOCAL_CREDS` (NetBSD) socket options. On other targets the `{set_}passcred()` methods do not exist. There are two problems with this approach: 1. Having public methods only exist for certain targets isn't permitted in a stable `std` API. 2. These options have generally similar purposes, but they are non-POSIX and their details can differ in subtle and surprising ways (such as whether they continue to be set after the next call to `recvmsg()`). Splitting into OS-specific extension traits is the preferred solution to both problems. --- library/std/src/os/freebsd/mod.rs | 1 + library/std/src/os/freebsd/net.rs | 65 +++++++++++++++++++ library/std/src/os/linux/net.rs | 3 + library/std/src/os/net/linux_ext/mod.rs | 3 + library/std/src/os/net/linux_ext/socket.rs | 63 ++++++++++++++++++ library/std/src/os/netbsd/mod.rs | 1 + library/std/src/os/netbsd/net.rs | 65 +++++++++++++++++++ library/std/src/os/unix/net/datagram.rs | 75 ++++------------------ library/std/src/os/unix/net/stream.rs | 75 ++++------------------ library/std/src/os/unix/net/tests.rs | 3 + library/std/src/sys/unix/net.rs | 26 +++++--- 11 files changed, 244 insertions(+), 136 deletions(-) create mode 100644 library/std/src/os/freebsd/net.rs create mode 100644 library/std/src/os/net/linux_ext/socket.rs create mode 100644 library/std/src/os/netbsd/net.rs diff --git a/library/std/src/os/freebsd/mod.rs b/library/std/src/os/freebsd/mod.rs index c072fae557fe0..39912e6970d45 100644 --- a/library/std/src/os/freebsd/mod.rs +++ b/library/std/src/os/freebsd/mod.rs @@ -3,4 +3,5 @@ #![stable(feature = "raw_ext", since = "1.1.0")] pub mod fs; +pub mod net; pub mod raw; diff --git a/library/std/src/os/freebsd/net.rs b/library/std/src/os/freebsd/net.rs new file mode 100644 index 0000000000000..ffd617e3e9d11 --- /dev/null +++ b/library/std/src/os/freebsd/net.rs @@ -0,0 +1,65 @@ +//! FreeBSD-specific networking functionality. + +#![unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + +use crate::io; +use crate::os::unix::net; +use crate::sealed::Sealed; +use crate::sys_common::AsInner; + +/// OpenBSD-specific functionality for `AF_UNIX` sockets [`UnixDatagram`] +/// and [`UnixStream`]. +/// +/// [`UnixDatagram`]: net::UnixDatagram +/// [`UnixStream`]: net::UnixStream +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +pub trait UnixSocketExt: Sealed { + /// Query the current setting of socket option `LOCAL_CREDS`. + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + fn local_creds_persistent(&self) -> io::Result; + + /// Enable or disable socket option `LOCAL_CREDS`. + /// + /// This option enables the credentials of the sending process to be + /// received as a control message in [`AncillaryData`]. + /// + /// [`AncillaryData`]: net::AncillaryData + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_ancillary_data)] + /// use std::os::freebsd::net::UnixSocketExt; + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.set_local_creds_persistent(true).expect("set_local_creds_persistent failed"); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()>; +} + +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +impl UnixSocketExt for net::UnixDatagram { + fn local_creds_persistent(&self) -> io::Result { + self.as_inner().local_creds_persistent() + } + + fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()> { + self.as_inner().set_local_creds_persistent(local_creds_persistent) + } +} + +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +impl UnixSocketExt for net::UnixStream { + fn local_creds_persistent(&self) -> io::Result { + self.as_inner().local_creds_persistent() + } + + fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()> { + self.as_inner().set_local_creds_persistent(local_creds_persistent) + } +} diff --git a/library/std/src/os/linux/net.rs b/library/std/src/os/linux/net.rs index c8e734d740b68..f898e70548706 100644 --- a/library/std/src/os/linux/net.rs +++ b/library/std/src/os/linux/net.rs @@ -5,5 +5,8 @@ #[stable(feature = "unix_socket_abstract", since = "1.70.0")] pub use crate::os::net::linux_ext::addr::SocketAddrExt; +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +pub use crate::os::net::linux_ext::socket::UnixSocketExt; + #[unstable(feature = "tcp_quickack", issue = "96256")] pub use crate::os::net::linux_ext::tcp::TcpStreamExt; diff --git a/library/std/src/os/net/linux_ext/mod.rs b/library/std/src/os/net/linux_ext/mod.rs index 62e78cc50d4ab..d0979640c32ea 100644 --- a/library/std/src/os/net/linux_ext/mod.rs +++ b/library/std/src/os/net/linux_ext/mod.rs @@ -5,6 +5,9 @@ #[stable(feature = "unix_socket_abstract", since = "1.70.0")] pub(crate) mod addr; +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +pub(crate) mod socket; + #[unstable(feature = "tcp_quickack", issue = "96256")] pub(crate) mod tcp; diff --git a/library/std/src/os/net/linux_ext/socket.rs b/library/std/src/os/net/linux_ext/socket.rs new file mode 100644 index 0000000000000..4e4168f693c34 --- /dev/null +++ b/library/std/src/os/net/linux_ext/socket.rs @@ -0,0 +1,63 @@ +//! Linux and Android-specific socket functionality. + +use crate::io; +use crate::os::unix::net; +use crate::sealed::Sealed; +use crate::sys_common::AsInner; + +/// Linux-specific functionality for `AF_UNIX` sockets [`UnixDatagram`] +/// and [`UnixStream`]. +/// +/// [`UnixDatagram`]: net::UnixDatagram +/// [`UnixStream`]: net::UnixStream +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +pub trait UnixSocketExt: Sealed { + /// Query the current setting of socket option `SO_PASSCRED`. + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + fn passcred(&self) -> io::Result; + + /// Enable or disable socket option `SO_PASSCRED`. + /// + /// This option enables the credentials of the sending process to be + /// received as a control message in [`AncillaryData`]. + /// + /// [`AncillaryData`]: net::AncillaryData + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_ancillary_data)] + /// use std::os::linux::net::UnixSocketExt; + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.set_passcred(true).expect("set_passcred failed"); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + fn set_passcred(&self, passcred: bool) -> io::Result<()>; +} + +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +impl UnixSocketExt for net::UnixDatagram { + fn passcred(&self) -> io::Result { + self.as_inner().passcred() + } + + fn set_passcred(&self, passcred: bool) -> io::Result<()> { + self.as_inner().set_passcred(passcred) + } +} + +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +impl UnixSocketExt for net::UnixStream { + fn passcred(&self) -> io::Result { + self.as_inner().passcred() + } + + fn set_passcred(&self, passcred: bool) -> io::Result<()> { + self.as_inner().set_passcred(passcred) + } +} diff --git a/library/std/src/os/netbsd/mod.rs b/library/std/src/os/netbsd/mod.rs index 497a51a1df6fd..2f21e98a6f4cb 100644 --- a/library/std/src/os/netbsd/mod.rs +++ b/library/std/src/os/netbsd/mod.rs @@ -3,4 +3,5 @@ #![stable(feature = "raw_ext", since = "1.1.0")] pub mod fs; +pub mod net; pub mod raw; diff --git a/library/std/src/os/netbsd/net.rs b/library/std/src/os/netbsd/net.rs new file mode 100644 index 0000000000000..d4c7b0627e135 --- /dev/null +++ b/library/std/src/os/netbsd/net.rs @@ -0,0 +1,65 @@ +//! OpenBSD-specific networking functionality. + +#![unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + +use crate::io; +use crate::os::unix::net; +use crate::sealed::Sealed; +use crate::sys_common::AsInner; + +/// OpenBSD-specific functionality for `AF_UNIX` sockets [`UnixDatagram`] +/// and [`UnixStream`]. +/// +/// [`UnixDatagram`]: net::UnixDatagram +/// [`UnixStream`]: net::UnixStream +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +pub trait UnixSocketExt: Sealed { + /// Query the current setting of socket option `LOCAL_CREDS`. + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + fn local_creds(&self) -> io::Result; + + /// Enable or disable socket option `LOCAL_CREDS`. + /// + /// This option enables the credentials of the sending process to be + /// received as a control message in [`AncillaryData`]. + /// + /// [`AncillaryData`]: net::AncillaryData + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_ancillary_data)] + /// use std::os::netbsd::net::UnixSocketExt; + /// use std::os::unix::net::UnixDatagram; + /// + /// fn main() -> std::io::Result<()> { + /// let sock = UnixDatagram::unbound()?; + /// sock.set_local_creds(true).expect("set_local_creds failed"); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] + fn set_local_creds(&self, local_creds: bool) -> io::Result<()>; +} + +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +impl UnixSocketExt for net::UnixDatagram { + fn local_creds(&self) -> io::Result { + self.as_inner().local_creds() + } + + fn set_local_creds(&self, local_creds: bool) -> io::Result<()> { + self.as_inner().set_local_creds(local_creds) + } +} + +#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] +impl UnixSocketExt for net::UnixStream { + fn local_creds(&self) -> io::Result { + self.as_inner().local_creds() + } + + fn set_local_creds(&self, local_creds: bool) -> io::Result<()> { + self.as_inner().set_local_creds(local_creds) + } +} diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index 34db54235f1c2..ab7cf066c904c 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -6,6 +6,7 @@ use crate::io::{IoSlice, IoSliceMut}; use crate::net::Shutdown; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::path::Path; +use crate::sealed::Sealed; use crate::sys::cvt; use crate::sys::net::Socket; use crate::sys_common::{AsInner, FromInner, IntoInner}; @@ -54,6 +55,10 @@ const MSG_NOSIGNAL: libc::c_int = 0x0; #[stable(feature = "unix_socket", since = "1.10.0")] pub struct UnixDatagram(Socket); +/// Allows extension traits within `std`. +#[unstable(feature = "sealed", issue = "none")] +impl Sealed for UnixDatagram {} + #[stable(feature = "unix_socket", since = "1.10.0")] impl fmt::Debug for UnixDatagram { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -802,69 +807,6 @@ impl UnixDatagram { self.0.set_nonblocking(nonblocking) } - /// Moves the socket to pass unix credentials as control message in [`SocketAncillary`]. - /// - /// Set the socket option `SO_PASSCRED`. - /// - /// # Examples - /// - #[cfg_attr( - any( - target_os = "android", - target_os = "linux", - target_os = "netbsd", - target_os = "freebsd", - ), - doc = "```no_run" - )] - #[cfg_attr( - not(any( - target_os = "android", - target_os = "linux", - target_os = "netbsd", - target_os = "freebsd" - )), - doc = "```ignore" - )] - /// #![feature(unix_socket_ancillary_data)] - /// use std::os::unix::net::UnixDatagram; - /// - /// fn main() -> std::io::Result<()> { - /// let sock = UnixDatagram::unbound()?; - /// sock.set_passcred(true).expect("set_passcred function failed"); - /// Ok(()) - /// } - /// ``` - #[cfg(any( - doc, - target_os = "android", - target_os = "linux", - target_os = "netbsd", - target_os = "freebsd" - ))] - #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] - pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { - self.0.set_passcred(passcred) - } - - /// Get the current value of the socket for passing unix credentials in [`SocketAncillary`]. - /// This value can be change by [`set_passcred`]. - /// - /// Get the socket option `SO_PASSCRED`. - /// - /// [`set_passcred`]: UnixDatagram::set_passcred - #[cfg(any( - doc, - target_os = "android", - target_os = "linux", - target_os = "netbsd", - target_os = "freebsd" - ))] - #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] - pub fn passcred(&self) -> io::Result { - self.0.passcred() - } - /// Set the id of the socket for network filtering purpose /// #[cfg_attr( @@ -1037,3 +979,10 @@ impl From for UnixDatagram { unsafe { Self::from_raw_fd(owned.into_raw_fd()) } } } + +impl AsInner for UnixDatagram { + #[inline] + fn as_inner(&self) -> &Socket { + &self.0 + } +} diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 41290e0017ac3..6d5f667a21b32 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -19,6 +19,7 @@ use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, Owned ))] use crate::os::unix::ucred; use crate::path::Path; +use crate::sealed::Sealed; use crate::sys::cvt; use crate::sys::net::Socket; use crate::sys_common::{AsInner, FromInner}; @@ -59,6 +60,10 @@ pub use ucred::UCred; #[stable(feature = "unix_socket", since = "1.10.0")] pub struct UnixStream(pub(super) Socket); +/// Allows extension traits within `std`. +#[unstable(feature = "sealed", issue = "none")] +impl Sealed for UnixStream {} + #[stable(feature = "unix_socket", since = "1.10.0")] impl fmt::Debug for UnixStream { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -394,69 +399,6 @@ impl UnixStream { self.0.set_nonblocking(nonblocking) } - /// Moves the socket to pass unix credentials as control message in [`SocketAncillary`]. - /// - /// Set the socket option `SO_PASSCRED`. - /// - /// # Examples - /// - #[cfg_attr( - any( - target_os = "android", - target_os = "linux", - target_os = "netbsd", - target_os = "freebsd" - ), - doc = "```no_run" - )] - #[cfg_attr( - not(any( - target_os = "android", - target_os = "linux", - target_os = "netbsd", - target_os = "freebsd" - )), - doc = "```ignore" - )] - /// #![feature(unix_socket_ancillary_data)] - /// use std::os::unix::net::UnixStream; - /// - /// fn main() -> std::io::Result<()> { - /// let socket = UnixStream::connect("/tmp/sock")?; - /// socket.set_passcred(true).expect("Couldn't set passcred"); - /// Ok(()) - /// } - /// ``` - #[cfg(any( - doc, - target_os = "android", - target_os = "linux", - target_os = "netbsd", - target_os = "freebsd" - ))] - #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] - pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { - self.0.set_passcred(passcred) - } - - /// Get the current value of the socket for passing unix credentials in [`SocketAncillary`]. - /// This value can be change by [`set_passcred`]. - /// - /// Get the socket option `SO_PASSCRED`. - /// - /// [`set_passcred`]: UnixStream::set_passcred - #[cfg(any( - doc, - target_os = "android", - target_os = "linux", - target_os = "netbsd", - target_os = "freebsd" - ))] - #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] - pub fn passcred(&self) -> io::Result { - self.0.passcred() - } - /// Set the id of the socket for network filtering purpose /// #[cfg_attr( @@ -765,3 +707,10 @@ impl From for UnixStream { unsafe { Self::from_raw_fd(owned.into_raw_fd()) } } } + +impl AsInner for UnixStream { + #[inline] + fn as_inner(&self) -> &Socket { + &self.0 + } +} diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index 6a6af9efd78ed..b413f3caa39bc 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -13,6 +13,9 @@ use crate::os::android::net::SocketAddrExt; #[cfg(target_os = "linux")] use crate::os::linux::net::SocketAddrExt; +#[cfg(any(target_os = "android", target_os = "linux"))] +use crate::os::linux::net::UnixSocketExt; + macro_rules! or_panic { ($e:expr) => { match $e { diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index ec861f9cb8696..d884a24c211ba 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -453,25 +453,31 @@ impl Socket { } #[cfg(target_os = "netbsd")] - pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { - setsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS, passcred as libc::c_int) + pub fn set_local_creds(&self, local_creds: bool) -> io::Result<()> { + setsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS, local_creds as libc::c_int) } #[cfg(target_os = "netbsd")] - pub fn passcred(&self) -> io::Result { - let passcred: libc::c_int = getsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS)?; - Ok(passcred != 0) + pub fn local_creds(&self) -> io::Result { + let local_creds: libc::c_int = getsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS)?; + Ok(local_creds != 0) } #[cfg(target_os = "freebsd")] - pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { - setsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT, passcred as libc::c_int) + pub fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()> { + setsockopt( + self, + libc::AF_LOCAL, + libc::LOCAL_CREDS_PERSISTENT, + local_creds_persistent as libc::c_int, + ) } #[cfg(target_os = "freebsd")] - pub fn passcred(&self) -> io::Result { - let passcred: libc::c_int = getsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT)?; - Ok(passcred != 0) + pub fn local_creds_persistent(&self) -> io::Result { + let local_creds_persistent: libc::c_int = + getsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT)?; + Ok(local_creds_persistent != 0) } #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "vita")))]