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