Skip to main content

zlink_core/connection/
socket.rs

1//! The low-level Socket read and write traits.
2
3use core::future::Future;
4
5#[cfg(feature = "std")]
6use std::os::fd::{AsFd, OwnedFd};
7
8/// The socket trait.
9///
10/// This is the trait that needs to be implemented for a type to be used as a socket/transport.
11pub trait Socket: core::fmt::Debug {
12    /// The read half of the socket.
13    type ReadHalf: ReadHalf;
14    /// The write half of the socket.
15    type WriteHalf: WriteHalf;
16
17    /// Whether this socket can transfer file descriptors.
18    ///
19    /// This is `true` for Unix domain sockets and `false` for other socket types.
20    const CAN_TRANSFER_FDS: bool = false;
21
22    /// Split the socket into read and write halves.
23    fn split(self) -> (Self::ReadHalf, Self::WriteHalf);
24}
25
26/// The read half of a socket.
27pub trait ReadHalf: core::fmt::Debug {
28    /// Read from a socket.
29    ///
30    /// On completion, the number of bytes read and any file descriptors received are returned.
31    ///
32    /// Notes for implementers:
33    ///
34    /// * The future returned by this method must be cancel safe.
35    /// * While there is no explicit `Unpin` bound on the future returned by this method, it is
36    ///   expected that it provides the same guarantees as `Unpin` would require. The reason `Unpin`
37    ///   is not explicitly required is that it would force boxing (and therefore allocation) on the
38    ///   implementation that use `async fn`, which is undesirable for embedded use cases. See [this
39    ///   issue](https://github.com/rust-lang/rust/issues/82187) for details.
40    fn read(&mut self, buf: &mut [u8]) -> impl Future<Output = crate::Result<ReadResult>>;
41}
42
43/// The write half of a socket.
44pub trait WriteHalf: core::fmt::Debug {
45    /// Write to the socket.
46    ///
47    /// The `fds` parameter contains file descriptors to send along with the data (std only).
48    ///
49    /// The returned future has the same requirements as that of [`ReadHalf::read`].
50    fn write(
51        &mut self,
52        buf: &[u8],
53        #[cfg(feature = "std")] fds: &[impl AsFd],
54        #[cfg(all(feature = "std", target_os = "linux"))] credentials: Option<
55            &crate::connection::PassedCredentials,
56        >,
57    ) -> impl Future<Output = crate::Result<()>>;
58}
59
60/// Trait for fetching peer credentials from a socket.
61///
62/// This trait provides the low-level capability to fetch credentials from a socket's underlying
63/// file descriptor. It is typically implemented by socket read halves that support credentials.
64#[cfg(feature = "std")]
65pub trait FetchPeerCredentials {
66    /// Fetch the peer credentials for this socket.
67    ///
68    /// This is the low-level method that socket implementations should override to provide peer
69    /// credentials. Higher-level APIs should use [`super::Connection::peer_credentials`] instead.
70    fn fetch_peer_credentials(&self) -> impl Future<Output = std::io::Result<super::Credentials>>;
71}
72
73/// Trait for Unix Domain Sockets.
74///
75/// Implementing this trait signals that the type is a Unix Domain Socket (UDS) where credentials
76/// fetching through a file descriptor will work correctly. [`FetchPeerCredentials`] is implemented
77/// for all types that implement this trait.
78#[cfg(feature = "std")]
79pub trait UnixSocket: AsFd {}
80
81#[cfg(feature = "std")]
82impl<T> FetchPeerCredentials for T
83where
84    T: UnixSocket,
85{
86    async fn fetch_peer_credentials(&self) -> std::io::Result<super::Credentials> {
87        // Assume peer credentials fetching never blocks so it's fine to call this synchronous
88        // method from an async context.
89        crate::unix_utils::get_peer_credentials(self)
90    }
91}
92
93/// Documentation-only socket implementations for doc tests.
94///
95/// These types exist only to make doc tests compile and should never be used in real code.
96#[doc(hidden)]
97pub mod impl_for_doc {
98
99    /// A mock socket for documentation examples.
100    #[derive(Debug)]
101    pub struct Socket;
102
103    impl super::Socket for Socket {
104        type ReadHalf = ReadHalf;
105        type WriteHalf = WriteHalf;
106
107        fn split(self) -> (Self::ReadHalf, Self::WriteHalf) {
108            (ReadHalf, WriteHalf)
109        }
110    }
111
112    /// A mock read half for documentation examples.
113    #[derive(Debug)]
114    pub struct ReadHalf;
115
116    impl super::ReadHalf for ReadHalf {
117        async fn read(&mut self, _buf: &mut [u8]) -> crate::Result<super::ReadResult> {
118            unreachable!("This is only for doc tests")
119        }
120    }
121
122    /// A mock write half for documentation examples.
123    #[derive(Debug)]
124    pub struct WriteHalf;
125
126    impl super::WriteHalf for WriteHalf {
127        async fn write(
128            &mut self,
129            _buf: &[u8],
130            #[cfg(feature = "std")] _fds: &[impl super::AsFd],
131            #[cfg(all(feature = "std", target_os = "linux"))] _credentials: Option<
132                &crate::connection::PassedCredentials,
133            >,
134        ) -> crate::Result<()> {
135            unreachable!("This is only for doc tests")
136        }
137    }
138}
139
140/// Result type for [`ReadHalf::read`] operations.
141#[derive(Debug)]
142pub struct ReadResult {
143    /// The number of bytes read.
144    bytes_read: usize,
145    /// The file descriptors received, if any. This is only available with the `std` feature.
146    #[cfg(feature = "std")]
147    fds: alloc::vec::Vec<OwnedFd>,
148    /// The credentials received, if any. This is only available with the `std` feature and `linux`
149    /// target.
150    #[cfg(all(feature = "std", target_os = "linux"))]
151    credentials: Option<crate::connection::PassedCredentials>,
152}
153
154impl ReadResult {
155    /// Creates a new `ReadResult` with the given number of bytes read.
156    #[doc(hidden)]
157    pub fn new(bytes_read: usize) -> Self {
158        Self {
159            bytes_read,
160            #[cfg(feature = "std")]
161            fds: vec![],
162            #[cfg(all(feature = "std", target_os = "linux"))]
163            credentials: None,
164        }
165    }
166
167    /// The number of bytes read.
168    pub fn bytes_read(&self) -> usize {
169        self.bytes_read
170    }
171
172    /// The file descriptors received, if any. This is only available with the `std` feature.
173    #[cfg(feature = "std")]
174    pub fn fds(&self) -> &[OwnedFd] {
175        &self.fds
176    }
177
178    /// The credentials received, if any. This is only available with the `std` feature and `linux`
179    /// target.
180    #[cfg(all(feature = "std", target_os = "linux"))]
181    pub fn credentials(&self) -> Option<&crate::connection::PassedCredentials> {
182        self.credentials.as_ref()
183    }
184
185    /// Sets the file descriptors received, if any. This is only available with the `std` feature.
186    #[cfg(feature = "std")]
187    #[doc(hidden)]
188    pub fn set_fds<F>(mut self, fds: F) -> Self
189    where
190        F: Into<alloc::vec::Vec<OwnedFd>>,
191    {
192        self.fds = fds.into();
193
194        self
195    }
196
197    /// Sets the credentials received, if any. This is only available with the `std` feature and
198    /// `linux` target.
199    #[cfg(all(feature = "std", target_os = "linux"))]
200    #[doc(hidden)]
201    pub fn set_credentials(
202        mut self,
203        credentials: Option<crate::connection::PassedCredentials>,
204    ) -> Self {
205        self.credentials = credentials;
206
207        self
208    }
209
210    /// Takes the file descriptors received, leaving an empty list in their place. This is only
211    /// available with the `std` feature.
212    #[cfg(feature = "std")]
213    pub fn take_fds(&mut self) -> alloc::vec::Vec<OwnedFd> {
214        core::mem::take(&mut self.fds)
215    }
216
217    /// Consumes this `ReadResult` and returns the file descriptors received, if any. This is only
218    /// available with the `std` feature.
219    #[cfg(feature = "std")]
220    pub fn into_fds(self) -> alloc::vec::Vec<OwnedFd> {
221        self.fds
222    }
223
224    /// Takes the credentials received, leaving `None` in their place. This is only available with
225    /// the `std` feature and `linux` target.
226    #[cfg(all(feature = "std", target_os = "linux"))]
227    pub fn take_credentials(&mut self) -> Option<crate::connection::PassedCredentials> {
228        self.credentials.take()
229    }
230
231    /// Consumes this `ReadResult` and returns the credentials received, if any. This is only
232    /// available with the `std` feature and `linux` target.
233    #[cfg(all(feature = "std", target_os = "linux"))]
234    pub fn into_credentials(self) -> Option<crate::connection::PassedCredentials> {
235        self.credentials
236    }
237}