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