Auto merge of #117156 - jmillikin:os-unix-socket-ext, r=Amanieu,dtolnay

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.
This commit is contained in:
bors 2024-03-11 07:46:01 +00:00
commit 6639672554
12 changed files with 246 additions and 138 deletions

View File

@ -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;

View File

@ -3,4 +3,5 @@
#![stable(feature = "raw_ext", since = "1.1.0")]
pub mod fs;
pub mod net;
pub mod raw;

View File

@ -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;
/// FreeBSD-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_PERSISTENT`.
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
fn local_creds_persistent(&self) -> io::Result<bool>;
/// Enable or disable socket option `LOCAL_CREDS_PERSISTENT`.
///
/// 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<bool> {
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<bool> {
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)
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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<bool>;
/// 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<bool> {
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<bool> {
self.as_inner().passcred()
}
fn set_passcred(&self, passcred: bool) -> io::Result<()> {
self.as_inner().set_passcred(passcred)
}
}

View File

@ -3,4 +3,5 @@
#![stable(feature = "raw_ext", since = "1.1.0")]
pub mod fs;
pub mod net;
pub mod raw;

View File

@ -0,0 +1,65 @@
//! NetBSD-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;
/// NetBSD-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<bool>;
/// 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<bool> {
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<bool> {
self.as_inner().local_creds()
}
fn set_local_creds(&self, local_creds: bool) -> io::Result<()> {
self.as_inner().set_local_creds(local_creds)
}
}

View File

@ -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<bool> {
self.0.passcred()
}
/// Set the id of the socket for network filtering purpose
///
#[cfg_attr(
@ -1038,3 +980,10 @@ impl From<OwnedFd> for UnixDatagram {
unsafe { Self::from_raw_fd(owned.into_raw_fd()) }
}
}
impl AsInner<Socket> for UnixDatagram {
#[inline]
fn as_inner(&self) -> &Socket {
&self.0
}
}

View File

@ -19,6 +19,7 @@ use crate::io::{self, 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};
@ -44,6 +45,10 @@ use crate::time::Duration;
#[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 {
@ -379,69 +384,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<bool> {
self.0.passcred()
}
/// Set the id of the socket for network filtering purpose
///
#[cfg_attr(
@ -751,3 +693,10 @@ impl From<OwnedFd> for UnixStream {
unsafe { Self::from_raw_fd(owned.into_raw_fd()) }
}
}
impl AsInner<Socket> for UnixStream {
#[inline]
fn as_inner(&self) -> &Socket {
&self.0
}
}

View File

@ -8,10 +8,10 @@ use crate::thread;
use crate::time::Duration;
#[cfg(target_os = "android")]
use crate::os::android::net::SocketAddrExt;
use crate::os::android::net::{SocketAddrExt, UnixSocketExt};
#[cfg(target_os = "linux")]
use crate::os::linux::net::SocketAddrExt;
use crate::os::linux::net::{SocketAddrExt, UnixSocketExt};
macro_rules! or_panic {
($e:expr) => {

View File

@ -465,25 +465,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<bool> {
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<bool> {
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<bool> {
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<bool> {
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")))]