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}