zx/
socket.rs

1// Copyright 2016 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//! Type-safe bindings for Zircon sockets.
6
7#![allow(clippy::bad_bit_mask)] // TODO(https://fxbug.dev/42080521): stop using bitflags for SocketOpts
8
9use crate::{
10    AsHandleRef, HandleBased, HandleRef, NullableHandle, ObjectQuery, Peered, Property,
11    PropertyQuery, Status, Topic, ok, sys,
12};
13use bitflags::bitflags;
14use std::mem::MaybeUninit;
15
16/// An object representing a Zircon
17/// [socket](https://fuchsia.dev/fuchsia-src/concepts/kernel/concepts#message_passing_sockets_and_channels)
18///
19/// As essentially a subtype of `NullableHandle`, it can be freely interconverted.
20#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
21#[repr(transparent)]
22pub struct Socket(NullableHandle);
23impl_handle_based!(Socket);
24impl Peered for Socket {}
25
26bitflags! {
27    #[repr(transparent)]
28    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
29    pub struct SocketOpts: u32 {
30        const STREAM = 0 << 0;
31        const DATAGRAM = 1 << 0;
32    }
33}
34
35bitflags! {
36    #[repr(transparent)]
37    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
38    pub struct SocketReadOpts: u32 {
39        const PEEK = 1 << 3;
40    }
41}
42
43bitflags! {
44    #[repr(transparent)]
45    #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
46    pub struct SocketWriteOpts: u32 {
47    }
48}
49
50/// Write disposition to set on a zircon socket with
51/// [zx_socket_set_disposition](https://fuchsia.dev/fuchsia-src/reference/syscalls/socket_set_disposition.md).
52#[derive(Debug, PartialEq, Eq, Clone, Copy)]
53pub enum SocketWriteDisposition {
54    /// Corresponds to `ZX_SOCKET_DISPOSITION_WRITE_ENABLED`.
55    Enabled,
56    /// Corresponds to `ZX_SOCKET_DISPOSITION_WRITE_DISABLED`.
57    Disabled,
58}
59
60impl SocketWriteDisposition {
61    /// Get a [`SocketWriteDisposition`] from the raw `u32` argument passed to
62    /// [zx_socket_set_disposition](https://fuchsia.dev/fuchsia-src/reference/syscalls/socket_set_disposition.md).
63    pub fn from_raw(raw: u32) -> Result<Option<SocketWriteDisposition>, Status> {
64        match raw {
65            0 => Ok(None),
66            sys::ZX_SOCKET_DISPOSITION_WRITE_ENABLED => Ok(Some(SocketWriteDisposition::Enabled)),
67            sys::ZX_SOCKET_DISPOSITION_WRITE_DISABLED => Ok(Some(SocketWriteDisposition::Disabled)),
68            _ => Err(Status::INVALID_ARGS),
69        }
70    }
71}
72
73impl From<SocketWriteDisposition> for u32 {
74    fn from(disposition: SocketWriteDisposition) -> Self {
75        match disposition {
76            SocketWriteDisposition::Enabled => sys::ZX_SOCKET_DISPOSITION_WRITE_ENABLED,
77            SocketWriteDisposition::Disabled => sys::ZX_SOCKET_DISPOSITION_WRITE_DISABLED,
78        }
79    }
80}
81
82#[repr(C)]
83#[derive(Debug, Copy, Clone, Eq, PartialEq)]
84pub struct SocketInfo {
85    pub options: SocketOpts,
86    pub rx_buf_max: usize,
87    pub rx_buf_size: usize,
88    pub rx_buf_available: usize,
89    pub tx_buf_max: usize,
90    pub tx_buf_size: usize,
91}
92
93impl Default for SocketInfo {
94    fn default() -> SocketInfo {
95        SocketInfo {
96            options: SocketOpts::STREAM,
97            rx_buf_max: 0,
98            rx_buf_size: 0,
99            rx_buf_available: 0,
100            tx_buf_max: 0,
101            tx_buf_size: 0,
102        }
103    }
104}
105
106impl From<sys::zx_info_socket_t> for SocketInfo {
107    fn from(socket: sys::zx_info_socket_t) -> SocketInfo {
108        SocketInfo {
109            options: SocketOpts::from_bits_truncate(socket.options),
110            rx_buf_max: socket.rx_buf_max,
111            rx_buf_size: socket.rx_buf_size,
112            rx_buf_available: socket.rx_buf_available,
113            tx_buf_max: socket.tx_buf_max,
114            tx_buf_size: socket.tx_buf_size,
115        }
116    }
117}
118
119// zx_info_socket_t is able to be safely replaced with a byte representation and is a PoD type.
120struct SocketInfoQuery;
121unsafe impl ObjectQuery for SocketInfoQuery {
122    const TOPIC: Topic = Topic::SOCKET;
123    type InfoTy = sys::zx_info_socket_t;
124}
125
126impl Socket {
127    /// Create a streaming socket, accessed through a pair of endpoints. Data written
128    /// into one may be read from the other.
129    ///
130    /// Wraps
131    /// [zx_socket_create](https://fuchsia.dev/fuchsia-src/reference/syscalls/socket_create.md).
132    ///
133    /// # Panics
134    ///
135    /// If the process' job policy denies socket creation or the kernel reports no memory
136    /// available to create a new socket.
137    pub fn create_stream() -> (Socket, Socket) {
138        Self::try_create(SocketOpts::STREAM).expect("socket creation can't fail with valid options")
139    }
140
141    /// Create a datagram socket, accessed through a pair of endpoints. Data written
142    /// into one may be read from the other.
143    ///
144    /// Wraps
145    /// [zx_socket_create](https://fuchsia.dev/fuchsia-src/reference/syscalls/socket_create.md).
146    ///
147    /// # Panics
148    ///
149    /// If the process' job policy denies socket creation or the kernel reports no memory
150    /// available to create a new socket.
151    pub fn create_datagram() -> (Socket, Socket) {
152        Self::try_create(SocketOpts::DATAGRAM)
153            .expect("socket creation can't fail with valid options")
154    }
155
156    fn try_create(sock_opts: SocketOpts) -> Result<(Socket, Socket), Status> {
157        unsafe {
158            let mut out0 = 0;
159            let mut out1 = 0;
160            let status = sys::zx_socket_create(sock_opts.bits(), &mut out0, &mut out1);
161            ok(status)?;
162            Ok((
163                Self::from(NullableHandle::from_raw(out0)),
164                Self::from(NullableHandle::from_raw(out1)),
165            ))
166        }
167    }
168
169    /// Write the given bytes into the socket.
170    ///
171    /// Return value (on success) is number of bytes actually written.
172    ///
173    /// Wraps
174    /// [zx_socket_write](https://fuchsia.dev/fuchsia-src/reference/syscalls/socket_write.md).
175    pub fn write(&self, bytes: &[u8]) -> Result<usize, Status> {
176        self.write_opts(bytes, SocketWriteOpts::default())
177    }
178
179    /// Write the given bytes into the socket.
180    ///
181    /// Return value (on success) is number of bytes actually written.
182    ///
183    /// # Safety
184    ///
185    /// `bytes` must be valid to read from for `len` bytes.
186    ///
187    /// Wraps
188    /// [zx_socket_write](https://fuchsia.dev/fuchsia-src/reference/syscalls/socket_write.md).
189    pub unsafe fn write_raw(&self, bytes: *const u8, len: usize) -> Result<usize, Status> {
190        // SAFETY: our caller is responsible for upholding this call's invariants
191        unsafe { self.write_raw_opts(bytes, len, SocketWriteOpts::default()) }
192    }
193
194    /// Write the given bytes into the socket, with options.
195    ///
196    /// Return value (on success) is number of bytes actually written.
197    ///
198    /// Wraps
199    /// [zx_socket_write](https://fuchsia.dev/fuchsia-src/reference/syscalls/socket_write.md).
200    pub fn write_opts(&self, bytes: &[u8], opts: SocketWriteOpts) -> Result<usize, Status> {
201        // SAFETY: this slice is valid to read from for `len` bytes.
202        unsafe { self.write_raw_opts(bytes.as_ptr(), bytes.len(), opts) }
203    }
204
205    /// Write the given bytes into the socket, with options.
206    ///
207    /// Return value (on success) is number of bytes actually written.
208    ///
209    /// # Safety
210    ///
211    /// `bytes` must be valid to read from for `len` bytes.
212    ///
213    /// Wraps
214    /// [zx_socket_write](https://fuchsia.dev/fuchsia-src/reference/syscalls/socket_write.md).
215    pub unsafe fn write_raw_opts(
216        &self,
217        bytes: *const u8,
218        len: usize,
219        opts: SocketWriteOpts,
220    ) -> Result<usize, Status> {
221        let mut actual = 0;
222        // SAFETY: this is an FFI call and our caller is responsible for upholding pointer validity.
223        let status = unsafe {
224            sys::zx_socket_write(self.raw_handle(), opts.bits(), bytes, len, &mut actual)
225        };
226        ok(status).map(|()| actual)
227    }
228
229    /// Read the given bytes from the socket.
230    /// Return value (on success) is number of bytes actually read.
231    ///
232    /// Wraps
233    /// [zx_socket_read](https://fuchsia.dev/fuchsia-src/reference/syscalls/socket_read.md).
234    pub fn read(&self, bytes: &mut [u8]) -> Result<usize, Status> {
235        self.read_opts(bytes, SocketReadOpts::default())
236    }
237
238    /// Read the given bytes from the socket, with options.
239    /// Return value (on success) is number of bytes actually read.
240    ///
241    /// Wraps
242    /// [zx_socket_read](https://fuchsia.dev/fuchsia-src/reference/syscalls/socket_read.md).
243    pub fn read_opts(&self, bytes: &mut [u8], opts: SocketReadOpts) -> Result<usize, Status> {
244        // SAFETY: `bytes` is valid to write to for its whole length.
245        unsafe { self.read_raw_opts(bytes.as_mut_ptr(), bytes.len(), opts) }
246    }
247
248    /// Read the given bytes from the socket.
249    ///
250    /// Return value (on success) is number of bytes actually read.
251    ///
252    /// # Safety
253    ///
254    /// `bytes` must be valid to write to for `len` bytes.
255    ///
256    /// Wraps
257    /// [zx_socket_read](https://fuchsia.dev/fuchsia-src/reference/syscalls/socket_read.md).
258    pub unsafe fn read_raw(&self, bytes: *mut u8, len: usize) -> Result<usize, Status> {
259        // SAFETY: our caller is responsible for this call's invariants
260        unsafe { self.read_raw_opts(bytes, len, SocketReadOpts::default()) }
261    }
262
263    /// Read the given bytes from the socket, with options.
264    ///
265    /// Return value (on success) is number of bytes actually read.
266    ///
267    /// # Safety
268    ///
269    /// `bytes` must be valid to write to for `len` bytes.
270    ///
271    /// Wraps
272    /// [zx_socket_read](https://fuchsia.dev/fuchsia-src/reference/syscalls/socket_read.md).
273    pub unsafe fn read_raw_opts(
274        &self,
275        bytes: *mut u8,
276        len: usize,
277        opts: SocketReadOpts,
278    ) -> Result<usize, Status> {
279        let mut actual = 0;
280        // SAFETY: our caller is responsible for upholding pointer invariants.
281        let status =
282            unsafe { sys::zx_socket_read(self.raw_handle(), opts.bits(), bytes, len, &mut actual) };
283        ok(status).map(|()| actual)
284    }
285
286    /// Like [`Socket::read_uninit_opts`] with default options.
287    pub fn read_uninit<'a>(
288        &self,
289        bytes: &'a mut [MaybeUninit<u8>],
290    ) -> Result<&'a mut [u8], Status> {
291        self.read_uninit_opts(bytes, SocketReadOpts::default())
292    }
293
294    /// Same as [Socket::read_opts], but reads into memory that might not be
295    /// initialized, returning an initialized slice of bytes on success.
296    pub fn read_uninit_opts<'a>(
297        &self,
298        bytes: &'a mut [MaybeUninit<u8>],
299        opts: SocketReadOpts,
300    ) -> Result<&'a mut [u8], Status> {
301        // SAFETY: `bytes` is valid to write to for its whole length.
302        // TODO(https://fxbug.dev/42079723) use MaybeUninit::slice_as_mut_ptr when stable.
303        let actual =
304            unsafe { self.read_raw_opts(bytes.as_mut_ptr().cast::<u8>(), bytes.len(), opts)? };
305        let (valid, _uninit) = bytes.split_at_mut(actual);
306
307        // SAFETY: the kernel has initialized all of `valid`'s bytes.
308        // TODO(https://fxbug.dev/42079723) use MaybeUninit::slice_assume_init_mut when stable.
309        Ok(unsafe { std::slice::from_raw_parts_mut(valid.as_mut_ptr().cast::<u8>(), valid.len()) })
310    }
311
312    /// Close half of the socket, so attempts by the other side to write will fail.
313    ///
314    /// Implements the `ZX_SOCKET_DISPOSITION_WRITE_DISABLED` option of
315    /// [zx_socket_set_disposition](https://fuchsia.dev/fuchsia-src/reference/syscalls/socket_set_disposition.md).
316    pub fn half_close(&self) -> Result<(), Status> {
317        self.set_disposition(None, Some(SocketWriteDisposition::Disabled))
318    }
319
320    /// Sets the disposition of write calls for a socket handle and its peer.
321    ///
322    /// Wraps
323    /// [zx_socket_set_disposition](https://fuchsia.dev/fuchsia-src/reference/syscalls/socket_set_disposition.md).
324    pub fn set_disposition(
325        &self,
326        disposition: Option<SocketWriteDisposition>,
327        disposition_peer: Option<SocketWriteDisposition>,
328    ) -> Result<(), Status> {
329        let status = unsafe {
330            sys::zx_socket_set_disposition(
331                self.raw_handle(),
332                disposition.map(u32::from).unwrap_or(0),
333                disposition_peer.map(u32::from).unwrap_or(0),
334            )
335        };
336        ok(status)
337    }
338
339    /// Returns the number of bytes available on the socket.
340    pub fn outstanding_read_bytes(&self) -> Result<usize, Status> {
341        Ok(self.info()?.rx_buf_available)
342    }
343
344    /// Wraps the
345    /// [zx_object_get_info](https://fuchsia.dev/fuchsia-src/reference/syscalls/object_get_info.md)
346    /// syscall for the ZX_INFO_SOCKET topic.
347    pub fn info(&self) -> Result<SocketInfo, Status> {
348        Ok(SocketInfo::from(self.0.get_info_single::<SocketInfoQuery>()?))
349    }
350}
351
352unsafe_handle_properties!(object: Socket,
353    props: [
354        {query_ty: SOCKET_RX_THRESHOLD, tag: SocketRxThresholdTag, prop_ty: usize, get:get_rx_threshold, set: set_rx_threshold},
355        {query_ty: SOCKET_TX_THRESHOLD, tag: SocketTxThresholdTag, prop_ty: usize, get:get_tx_threshold, set: set_tx_threshold},
356    ]
357);
358
359// TODO(wesleyac): Test peeking
360
361#[cfg(test)]
362mod tests {
363    use std::mem::MaybeUninit;
364
365    use super::*;
366
367    fn socket_basic_helper(opts: SocketOpts) {
368        let (s1, s2) = match opts {
369            SocketOpts::STREAM => Socket::create_stream(),
370            SocketOpts::DATAGRAM => Socket::create_datagram(),
371            _ => panic!("unsupported socket options"),
372        };
373
374        // Write two packets and read from other end
375        assert_eq!(s1.write(b"hello").unwrap(), 5);
376        assert_eq!(s1.write(b"world").unwrap(), 5);
377
378        let mut read_vec = vec![0; 11];
379        if opts == SocketOpts::DATAGRAM {
380            assert_eq!(s2.read(&mut read_vec).unwrap(), 5);
381            assert_eq!(&read_vec[0..5], b"hello");
382
383            assert_eq!(s2.read(&mut read_vec).unwrap(), 5);
384            assert_eq!(&read_vec[0..5], b"world");
385        } else {
386            assert_eq!(s2.read(&mut read_vec).unwrap(), 10);
387            assert_eq!(&read_vec[0..10], b"helloworld");
388        }
389
390        // Try reading when there is nothing to read.
391        assert_eq!(s2.read(&mut read_vec), Err(Status::SHOULD_WAIT));
392
393        // Disable writes from the socket peer.
394        assert!(s1.half_close().is_ok());
395        assert_eq!(s2.write(b"fail"), Err(Status::BAD_STATE));
396        assert_eq!(s1.read(&mut read_vec), Err(Status::BAD_STATE));
397
398        // Writing to the peer should still work.
399        assert_eq!(s2.read(&mut read_vec), Err(Status::SHOULD_WAIT));
400        assert_eq!(s1.write(b"back").unwrap(), 4);
401        assert_eq!(s2.read(&mut read_vec).unwrap(), 4);
402        assert_eq!(&read_vec[0..4], b"back");
403    }
404
405    #[test]
406    fn socket_basic() {
407        socket_basic_helper(SocketOpts::STREAM);
408        socket_basic_helper(SocketOpts::DATAGRAM);
409    }
410
411    #[test]
412    fn socket_info() {
413        let (s1, s2) = Socket::create_stream();
414        let s1info = s1.info().unwrap();
415        // Socket should be empty.
416        assert_eq!(s1info.rx_buf_available, 0);
417        assert_eq!(s1info.rx_buf_size, 0);
418        assert_eq!(s1info.tx_buf_size, 0);
419
420        // Put some data in one end.
421        assert_eq!(s1.write(b"hello").unwrap(), 5);
422
423        // We should see the info change on each end correspondingly.
424        let s1info = s1.info().unwrap();
425        let s2info = s2.info().unwrap();
426        assert_eq!(s1info.tx_buf_size, 5);
427        assert_eq!(s1info.rx_buf_size, 0);
428        assert_eq!(s2info.rx_buf_size, 5);
429        assert_eq!(s2info.rx_buf_available, 5);
430        assert_eq!(s2info.tx_buf_size, 0);
431    }
432
433    #[test]
434    fn socket_disposition() {
435        const PAYLOAD: &'static [u8] = b"Hello";
436        let (s1, s2) = Socket::create_stream();
437        // Disable write on s1 but enable on s2.
438        assert_eq!(
439            s1.set_disposition(
440                Some(SocketWriteDisposition::Disabled),
441                Some(SocketWriteDisposition::Enabled)
442            ),
443            Ok(())
444        );
445        assert_eq!(s2.write(PAYLOAD), Ok(PAYLOAD.len()));
446        assert_eq!(s1.write(PAYLOAD), Err(Status::BAD_STATE));
447        let mut buf = [0u8; PAYLOAD.len() + 1];
448        assert_eq!(s1.read(&mut buf[..]), Ok(PAYLOAD.len()));
449        assert_eq!(&buf[..PAYLOAD.len()], PAYLOAD);
450        // Setting disposition to None changes nothing.
451        assert_eq!(s1.set_disposition(None, None), Ok(()));
452        assert_eq!(s2.write(PAYLOAD), Ok(PAYLOAD.len()));
453        assert_eq!(s1.write(PAYLOAD), Err(Status::BAD_STATE));
454    }
455
456    #[test]
457    fn read_uninit() {
458        let (s1, s2) = Socket::create_stream();
459        let message = b"hello";
460        assert_eq!(s1.write(&message[..]).unwrap(), 5);
461        let mut recv = [MaybeUninit::<u8>::uninit(); 16];
462        let got = s2.read_uninit(&mut recv[..]).unwrap();
463        assert_eq!(got, &message[..]);
464    }
465}