fdomain_container/
handles.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#[cfg(target_os = "fuchsia")]
6use fidl::HandleBased;
7use fidl::{AsHandleRef, Peered, SocketOpts};
8use fidl_fuchsia_fdomain as proto;
9
10/// Amount of buffer space we allocate for reading from handles in order to
11/// serve read requests.
12const READ_BUFFER_SIZE: usize = 4096;
13
14/// This is implemented on the `fidl::*` objects for every type of handle FDomain
15/// supports. It essentially makes the handle object a responder to a stream of
16/// [`HandleOperation`]s.
17pub trait HandleType:
18    Sync + Sized + Into<fidl::NullableHandle> + fidl::AsHandleRef + 'static
19{
20    /// This should be the handle type corresponding to the implementing handle.
21    /// We use this to generalize some of the error reporting in this trait.
22    fn object_type(&self) -> fidl::ObjectType;
23
24    /// Returns `Ok` if this handle is the given type, `Err` otherwise.
25    fn expected_type(&self, expected_type: fidl::ObjectType) -> Result<(), proto::Error> {
26        if expected_type == self.object_type() {
27            Ok(())
28        } else {
29            Err(proto::Error::WrongHandleType(proto::WrongHandleType {
30                expected: expected_type,
31                got: self.object_type(),
32            }))
33        }
34    }
35
36    /// Implements [`HandleOperation::SocketDisposition`].
37    fn socket_disposition(
38        &self,
39        _disposition: proto::SocketDisposition,
40        _disposition_peer: proto::SocketDisposition,
41    ) -> Result<(), proto::Error> {
42        self.expected_type(fidl::ObjectType::SOCKET)
43    }
44
45    /// Implements [`HandleOperation::ReadSocket`].
46    fn read_socket(&self, _max_bytes: u64) -> Result<Option<Vec<u8>>, proto::Error> {
47        Err(self.expected_type(fidl::ObjectType::SOCKET).unwrap_err())
48    }
49
50    /// Implements [`HandleOperation::ReadChannel`].
51    fn read_channel(&self) -> Result<Option<fidl::MessageBufEtc>, proto::Error> {
52        Err(self.expected_type(fidl::ObjectType::CHANNEL).unwrap_err())
53    }
54
55    /// Implements [`HandleOperation::WriteSocket`].
56    fn write_socket(&self, _data: &[u8]) -> Result<usize, proto::Error> {
57        Err(self.expected_type(fidl::ObjectType::SOCKET).unwrap_err())
58    }
59
60    /// Implements [`HandleOperation::WriteChannel`].
61    fn write_channel(
62        &self,
63        _data: &[u8],
64        _handles: &mut Vec<fidl::HandleDisposition<'static>>,
65    ) -> Option<Result<(), proto::WriteChannelError>> {
66        Some(Err(proto::WriteChannelError::Error(
67            self.expected_type(fidl::ObjectType::CHANNEL).unwrap_err(),
68        )))
69    }
70}
71
72impl HandleType for fidl::Socket {
73    fn object_type(&self) -> fidl::ObjectType {
74        fidl::ObjectType::SOCKET
75    }
76
77    #[cfg(not(target_os = "fuchsia"))]
78    fn socket_disposition(
79        &self,
80        _disposition: proto::SocketDisposition,
81        _disposition_peer: proto::SocketDisposition,
82    ) -> Result<(), proto::Error> {
83        Err(proto::Error::TargetError(fidl::Status::NOT_SUPPORTED.into_raw()))
84    }
85
86    #[cfg(target_os = "fuchsia")]
87    fn socket_disposition(
88        &self,
89        disposition: proto::SocketDisposition,
90        disposition_peer: proto::SocketDisposition,
91    ) -> Result<(), proto::Error> {
92        fn map_disposition(
93            disposition: proto::SocketDisposition,
94        ) -> Result<Option<zx::SocketWriteDisposition>, proto::Error> {
95            match disposition {
96                proto::SocketDisposition::NoChange => Ok(None),
97                proto::SocketDisposition::WriteDisabled => {
98                    Ok(Some(zx::SocketWriteDisposition::Disabled))
99                }
100                proto::SocketDisposition::WriteEnabled => {
101                    Ok(Some(zx::SocketWriteDisposition::Enabled))
102                }
103                disposition => {
104                    Err(proto::Error::SocketDispositionUnknown(proto::SocketDispositionUnknown {
105                        disposition,
106                    }))
107                }
108            }
109        }
110
111        self.set_disposition(map_disposition(disposition)?, map_disposition(disposition_peer)?)
112            .map_err(|e| proto::Error::TargetError(e.into_raw()))
113    }
114
115    fn read_socket(&self, max_bytes: u64) -> Result<Option<Vec<u8>>, proto::Error> {
116        let mut buf = [0u8; READ_BUFFER_SIZE];
117        let buf = if max_bytes < READ_BUFFER_SIZE as u64 {
118            &mut buf[..max_bytes as usize]
119        } else {
120            &mut buf
121        };
122        match self.read(buf) {
123            Ok(size) => Ok(Some(buf[..size].to_vec())),
124            Err(fidl::Status::SHOULD_WAIT) => Ok(None),
125            Err(other) => Err(proto::Error::TargetError(other.into_raw())),
126        }
127    }
128
129    fn write_socket(&self, data: &[u8]) -> Result<usize, proto::Error> {
130        let mut wrote = 0;
131        loop {
132            match self.write(&data[wrote..]) {
133                Ok(count) => {
134                    wrote += count;
135
136                    if wrote >= data.len() {
137                        break Ok(wrote);
138                    }
139                }
140                Err(fidl::Status::SHOULD_WAIT) => break Ok(wrote),
141
142                Err(other) => break Err(proto::Error::TargetError(other.into_raw())),
143            }
144        }
145    }
146}
147
148impl HandleType for fidl::Channel {
149    fn object_type(&self) -> fidl::ObjectType {
150        fidl::ObjectType::CHANNEL
151    }
152
153    fn read_channel(&self) -> Result<Option<fidl::MessageBufEtc>, proto::Error> {
154        let mut buf = fidl::MessageBufEtc::new();
155        match self.read_etc(&mut buf) {
156            Err(fidl::Status::SHOULD_WAIT) => Ok(None),
157            other => other.map(|_| Some(buf)).map_err(|e| proto::Error::TargetError(e.into_raw())),
158        }
159    }
160
161    fn write_channel(
162        &self,
163        data: &[u8],
164        handles: &mut Vec<fidl::HandleDisposition<'static>>,
165    ) -> Option<Result<(), proto::WriteChannelError>> {
166        match self.write_etc(data, handles) {
167            Ok(()) => Some(Ok(())),
168            Err(fidl::Status::SHOULD_WAIT) => None,
169            Err(other) => {
170                if handles.iter().any(|x| x.result != fidl::Status::OK) {
171                    Some(Err(proto::WriteChannelError::OpErrors(
172                        handles
173                            .into_iter()
174                            .map(|x| {
175                                Result::from(x.result)
176                                    .err()
177                                    .map(|e| Box::new(proto::Error::TargetError(e.into_raw())))
178                            })
179                            .collect(),
180                    )))
181                } else {
182                    Some(Err(proto::WriteChannelError::Error(proto::Error::TargetError(
183                        other.into_raw(),
184                    ))))
185                }
186            }
187        }
188    }
189}
190
191impl HandleType for fidl::EventPair {
192    fn object_type(&self) -> fidl::ObjectType {
193        fidl::ObjectType::EVENTPAIR
194    }
195}
196
197impl HandleType for fidl::Event {
198    fn object_type(&self) -> fidl::ObjectType {
199        fidl::ObjectType::EVENT
200    }
201}
202
203pub struct Unknown(pub fidl::NullableHandle, pub fidl::ObjectType);
204
205impl Into<fidl::NullableHandle> for Unknown {
206    fn into(self) -> fidl::NullableHandle {
207        self.0
208    }
209}
210
211impl fidl::AsHandleRef for Unknown {
212    fn as_handle_ref(&self) -> fidl::HandleRef<'_> {
213        self.0.as_handle_ref()
214    }
215}
216
217impl HandleType for Unknown {
218    fn object_type(&self) -> fidl::ObjectType {
219        self.1
220    }
221}
222
223pub enum AnyHandle {
224    Socket(fidl::Socket),
225    EventPair(fidl::EventPair),
226    Event(fidl::Event),
227    Channel(fidl::Channel),
228    Unknown(Unknown),
229}
230
231/// Whether a socket is a datagram socket.
232#[derive(Debug, Copy, Clone, PartialEq, Eq)]
233pub(crate) enum IsDatagramSocket {
234    NotDatagram,
235    Datagram,
236    Unknown,
237}
238
239impl IsDatagramSocket {
240    pub fn is_datagram(&self) -> bool {
241        matches!(self, IsDatagramSocket::Datagram)
242    }
243}
244
245impl AnyHandle {
246    /// Signals that indicate we should do write processing on a handle.
247    pub(crate) fn write_signals(&self) -> fidl::Signals {
248        let sock_signals = if let AnyHandle::Socket(_) = self {
249            fidl::Signals::SOCKET_WRITE_DISABLED
250        } else {
251            fidl::Signals::empty()
252        };
253
254        sock_signals | fidl::Signals::OBJECT_WRITABLE | fidl::Signals::OBJECT_PEER_CLOSED
255    }
256
257    /// Signals that indicate we should do write processing on a handle.
258    pub(crate) fn read_signals(&self) -> fidl::Signals {
259        let sock_signals = if let AnyHandle::Socket(_) = self {
260            fidl::Signals::SOCKET_PEER_WRITE_DISABLED
261        } else {
262            fidl::Signals::empty()
263        };
264
265        sock_signals | fidl::Signals::OBJECT_READABLE | fidl::Signals::OBJECT_PEER_CLOSED
266    }
267
268    /// Whether this is a datagram socket.
269    pub(crate) fn is_datagram_socket(&self) -> IsDatagramSocket {
270        let AnyHandle::Socket(socket) = self else {
271            return IsDatagramSocket::NotDatagram;
272        };
273
274        let Ok(info) = socket.info() else {
275            return IsDatagramSocket::Unknown;
276        };
277
278        if info.options == SocketOpts::DATAGRAM {
279            IsDatagramSocket::Datagram
280        } else {
281            IsDatagramSocket::NotDatagram
282        }
283    }
284
285    /// zx_handle_duplicate but preserving our metadata.
286    pub fn duplicate(&self, rights: fidl::Rights) -> Result<AnyHandle, proto::Error> {
287        let handle = self
288            .as_handle_ref()
289            .duplicate(rights)
290            .map_err(|e| proto::Error::TargetError(e.into_raw()))?;
291
292        Ok(match self {
293            AnyHandle::Socket(_) => AnyHandle::Socket(fidl::Socket::from(handle)),
294            AnyHandle::EventPair(_) => AnyHandle::EventPair(fidl::EventPair::from(handle)),
295            AnyHandle::Event(_) => AnyHandle::Event(fidl::Event::from(handle)),
296            AnyHandle::Channel(_) => AnyHandle::Channel(fidl::Channel::from(handle)),
297            AnyHandle::Unknown(Unknown(_, ty)) => AnyHandle::Unknown(Unknown(handle, *ty)),
298        })
299    }
300
301    /// zx_handle_replace but preserving our metadata.
302    #[cfg(not(target_os = "fuchsia"))]
303    pub fn replace(self, _rights: fidl::Rights) -> Result<AnyHandle, proto::Error> {
304        Err(proto::Error::TargetError(fidl::Status::NOT_SUPPORTED.into_raw()))
305    }
306
307    /// zx_handle_replace but preserving our metadata.
308    #[cfg(target_os = "fuchsia")]
309    pub fn replace(self, rights: fidl::Rights) -> Result<AnyHandle, proto::Error> {
310        Ok(match self {
311            AnyHandle::Socket(h) => AnyHandle::Socket(
312                h.replace_handle(rights).map_err(|e| proto::Error::TargetError(e.into_raw()))?,
313            ),
314            AnyHandle::EventPair(h) => AnyHandle::EventPair(
315                h.replace_handle(rights).map_err(|e| proto::Error::TargetError(e.into_raw()))?,
316            ),
317            AnyHandle::Event(h) => AnyHandle::Event(
318                h.replace_handle(rights).map_err(|e| proto::Error::TargetError(e.into_raw()))?,
319            ),
320            AnyHandle::Channel(h) => AnyHandle::Channel(
321                h.replace_handle(rights).map_err(|e| proto::Error::TargetError(e.into_raw()))?,
322            ),
323            AnyHandle::Unknown(Unknown(h, ty)) => AnyHandle::Unknown(Unknown(
324                h.replace(rights).map_err(|e| proto::Error::TargetError(e.into_raw()))?,
325                ty,
326            )),
327        })
328    }
329
330    pub fn signal_peer(
331        &self,
332        clear: fidl::Signals,
333        set: fidl::Signals,
334    ) -> Result<(), proto::Error> {
335        // TODO: Rust is being helpful and only letting us signal peers for
336        // things we know have them, but we'd really like to just make the
337        // syscall and get it to report whatever error it will. Especially for
338        // `Unknown`, which may well have peers the type system just isn't aware
339        // of.
340        match self {
341            AnyHandle::Socket(h) => h.signal_peer(clear, set),
342            AnyHandle::EventPair(h) => h.signal_peer(clear, set),
343            AnyHandle::Event(_) => Err(fidl::Status::INVALID_ARGS),
344            AnyHandle::Channel(h) => h.signal_peer(clear, set),
345            AnyHandle::Unknown(_) => Err(fidl::Status::INVALID_ARGS),
346        }
347        .map_err(|e| proto::Error::TargetError(e.into_raw()))
348    }
349}
350
351impl From<fidl::Channel> for AnyHandle {
352    fn from(other: fidl::Channel) -> AnyHandle {
353        AnyHandle::Channel(other)
354    }
355}
356
357impl From<fidl::Socket> for AnyHandle {
358    fn from(other: fidl::Socket) -> AnyHandle {
359        AnyHandle::Socket(other)
360    }
361}
362
363impl From<fidl::EventPair> for AnyHandle {
364    fn from(other: fidl::EventPair) -> AnyHandle {
365        AnyHandle::EventPair(other)
366    }
367}
368
369impl From<fidl::Event> for AnyHandle {
370    fn from(other: fidl::Event) -> AnyHandle {
371        AnyHandle::Event(other)
372    }
373}
374
375macro_rules! impl_method {
376    ($this:ident => $h:ident . $meth:ident ( $($args:tt)* )) => {
377        match $this {
378            AnyHandle::Socket($h) => $h.$meth($($args)*),
379            AnyHandle::EventPair($h) => $h.$meth($($args)*),
380            AnyHandle::Event($h) => $h.$meth($($args)*),
381            AnyHandle::Channel($h) => $h.$meth($($args)*),
382            AnyHandle::Unknown($h) => $h.$meth($($args)*),
383        }
384    };
385}
386
387impl HandleType for AnyHandle {
388    fn object_type(&self) -> fidl::ObjectType {
389        impl_method!(self => h.object_type())
390    }
391
392    fn socket_disposition(
393        &self,
394        disposition: proto::SocketDisposition,
395        disposition_peer: proto::SocketDisposition,
396    ) -> Result<(), proto::Error> {
397        impl_method!(self => h.socket_disposition(disposition, disposition_peer))
398    }
399
400    fn read_socket(&self, max_bytes: u64) -> Result<Option<Vec<u8>>, proto::Error> {
401        impl_method!(self => h.read_socket(max_bytes))
402    }
403
404    fn read_channel(&self) -> Result<Option<fidl::MessageBufEtc>, proto::Error> {
405        impl_method!(self => h.read_channel())
406    }
407
408    fn write_socket(&self, data: &[u8]) -> Result<usize, proto::Error> {
409        impl_method!(self => h.write_socket(data))
410    }
411
412    fn write_channel(
413        &self,
414        data: &[u8],
415        handles: &mut Vec<fidl::HandleDisposition<'static>>,
416    ) -> Option<Result<(), proto::WriteChannelError>> {
417        impl_method!(self => h.write_channel(data, handles))
418    }
419}
420
421impl fidl::AsHandleRef for AnyHandle {
422    fn as_handle_ref(&self) -> fidl::HandleRef<'_> {
423        impl_method!(self => h.as_handle_ref())
424    }
425}
426
427impl Into<fidl::NullableHandle> for AnyHandle {
428    fn into(self) -> fidl::NullableHandle {
429        impl_method!(self => h.into())
430    }
431}