From b6549a605935e29ab0ae4291737f8b0158bca1fb Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Wed, 25 Mar 2020 08:38:01 +0000 Subject: recursive deserialization --- msg_socket2/src/de.rs | 731 +++++++++++++++++++++++++++++++++++++++++++++- msg_socket2/src/error.rs | 3 + msg_socket2/src/fd.rs | 59 ++++ msg_socket2/src/lib.rs | 55 ++-- msg_socket2/src/ser.rs | 147 +++++++--- msg_socket2/src/socket.rs | 18 +- 6 files changed, 934 insertions(+), 79 deletions(-) create mode 100644 msg_socket2/src/fd.rs (limited to 'msg_socket2/src') diff --git a/msg_socket2/src/de.rs b/msg_socket2/src/de.rs index 1d3a9e1..0dec746 100644 --- a/msg_socket2/src/de.rs +++ b/msg_socket2/src/de.rs @@ -1,21 +1,730 @@ -use serde::Deserializer; -use std::os::unix::prelude::*; +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright 2020, Alyssa Ross +// +// Portions Copyright Erick Tryzelaar and David Tolnay. +// Licensed under either of Apache License, Version 2.0 +// or MIT license at your option. +//! Data structure deserialization framework for Unix domain sockets. +//! +//! Much like `serde::de`, except receiving file descriptors is also +//! supported. + +use crate::Fd; +use std::cmp::min; +use std::collections::BTreeMap; +use std::fmt::{self, Formatter}; +use std::marker::PhantomData; + +pub use serde::de::{ + Deserialize, DeserializeSeed, Deserializer, EnumAccess, Error, SeqAccess, StdError, + VariantAccess, Visitor, +}; + +/// A generic container for an inner serde type, and some file descriptors. +/// +/// Useful for implementing assorted `*WithFds` traits in terms of +/// their serde equivalents. +#[derive(Debug)] +pub(crate) struct WithFds<'fds, T, F> { + pub inner: T, + pub fds: &'fds mut F, +} + +/// Like `Deserialize`, but provides file descriptors as well as data. pub trait DeserializeWithFds<'de>: Sized { - fn deserialize(deserializer: DeserializerWithFds) -> Result + /// Like `Deserialize::deserialize`, but `deserializer` is a + /// `DeserializerWithFds` instead of a `Deserializer`. + fn deserialize>(deserializer: D) -> Result; +} + +/// Like `DeserializeSeed`, but provides file descriptors as well as +/// data. +pub trait DeserializeWithFdsSeed<'de>: Sized { + /// Like `DeserializeSeed::Value`. + type Value; + + /// Like `DeserializeSeed::deserialize`, but `deserializer` is a + /// `DeserializerWithFds` instead of a `Deserializer`. + fn deserialize(self, deserializer: D) -> Result + where + D: DeserializerWithFds<'de>; +} + +impl<'de, T: DeserializeWithFds<'de>> DeserializeWithFdsSeed<'de> for PhantomData { + type Value = T; + + fn deserialize(self, deserializer: D) -> Result + where + D: DeserializerWithFds<'de>, + { + T::deserialize(deserializer) + } +} + +impl<'fds, 'de, T, F> DeserializeSeed<'de> for WithFds<'fds, T, F> +where + F: Iterator, + T: DeserializeWithFdsSeed<'de>, +{ + type Value = T::Value; + + fn deserialize>(self, deserializer: D) -> Result { + let deserializer_with_fds = WithFds { + inner: deserializer, + fds: self.fds, + }; + + self.inner.deserialize(deserializer_with_fds) + } +} + +/// Like `Deserializer` but provides file descriptors as well as data. +pub trait DeserializerWithFds<'de> { + /// Like `Deserializer::Error`. + type Error: Error; + + /// Deserialize a file descriptor. + fn deserialize_fd(self) -> Result; + + /// Like `Deserializer::deserialize_option`, but `visitor` is a + /// `VisitorWithFds` instead of a `Visitor`. + fn deserialize_option(self, visitor: V) -> Result where - I: Iterator, - De: Deserializer<'de>; + V: VisitorWithFds<'de>; + + /// Like `Deserializer::deserialize_seq` but `visitor` is a + /// `VisitorWithFds` instead of a `Visitor`. + fn deserialize_seq>(self, visitor: V) -> Result; + + /// Like `Deserializer::deserialize_map` but `visitor` is a + /// `VisitorWithFds` instead of a `Visitor`. + fn deserialize_tuple_struct>( + self, + name: &'static str, + len: usize, + visitor: V, + ) -> Result; + + /// Like `Deserializer::deserialize_map` but `visitor` is a + /// `VisitorWithFds` instead of a `Visitor`. + fn deserialize_map>(self, visitor: V) -> Result; + + /// Like `Deserializer::deserialize_struct` but `visitor` is a + /// `VisitorWithFds` instead of a `Visitor`. + fn deserialize_struct>( + self, + name: &'static str, + fields: &'static [&'static str], + visitor: V, + ) -> Result; + + /// Like `Deserializer::deserialize_enum` but `visitor` is a + /// `VisitorWithFds` instead of a `Visitor`. + fn deserialize_enum>( + self, + name: &'static str, + variants: &'static [&'static str], + visitor: V, + ) -> Result; + + /// Invite a `Visitor` to visit a `Some` value. + /// + /// The purpose of this indirection is to allow implementations to + /// control how `Visitor::visit_some` is called. This means that + /// whatever way they get a `Deserializer` to pass to it doesn't + /// have to be exposed publicly through the trait. + /// + /// This looks a lot like a `deserialize_*` method, but it takes a + /// `Visitor` instead of a `VisitorWithFds`, and it's not part of + /// the serde interface. + fn invite_visit_some>(self, visitor: V) -> Result; + + /// Invite a `Deserialize` to deserialize. + /// + /// The purpose of this indirection is to allow implementations to + /// control how `Deserialize::deserialize` is called. This means + /// that whatever way they get a `Deserializer` to pass to it + /// doesn't have to be exposed publicly through the trait. + fn invite_deserialize>(self) -> Result; +} + +impl<'fds, 'de, D, F> DeserializerWithFds<'de> for WithFds<'fds, D, F> +where + D: Deserializer<'de>, + F: Iterator, +{ + type Error = D::Error; + + fn deserialize_fd(self) -> Result { + struct UnitVisitor; + + impl<'de> Visitor<'de> for UnitVisitor { + type Value = (); + + fn expecting(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "expecting unit") + } + + fn visit_unit(self) -> Result { + Ok(()) + } + } + + // This is probably not necessary, since unit should be + // serialized to nothing, but it's more correct to do this, + // because unit was serialized, and so unit should be + // deserialized. + self.inner.deserialize_unit(UnitVisitor)?; + + self.fds + .next() + .ok_or_else(|| Self::Error::custom("no fds to deserialize")) + } + + fn deserialize_option(self, visitor: V) -> Result + where + V: VisitorWithFds<'de>, + { + let wrapper = WithFds { + inner: visitor, + fds: self.fds, + }; + + self.inner.deserialize_option(wrapper) + } + + fn deserialize_seq>(self, visitor: V) -> Result { + let wrapper = WithFds { + inner: visitor, + fds: self.fds, + }; + + self.inner.deserialize_seq(wrapper) + } + + fn deserialize_tuple_struct>( + self, + name: &'static str, + len: usize, + visitor: V, + ) -> Result { + let wrapper = WithFds { + inner: visitor, + fds: self.fds, + }; + + self.inner.deserialize_tuple_struct(name, len, wrapper) + } + + fn deserialize_map>(self, visitor: V) -> Result { + let wrapper = WithFds { + inner: visitor, + fds: self.fds, + }; + + self.inner.deserialize_map(wrapper) + } + + fn deserialize_struct>( + self, + name: &'static str, + fields: &'static [&'static str], + visitor: V, + ) -> Result { + let wrapper = WithFds { + inner: visitor, + fds: self.fds, + }; + + self.inner.deserialize_struct(name, fields, wrapper) + } + + fn deserialize_enum>( + self, + name: &'static str, + variants: &'static [&'static str], + visitor: V, + ) -> Result { + let wrapper = WithFds { + inner: visitor, + fds: self.fds, + }; + + self.inner.deserialize_enum(name, variants, wrapper) + } + + fn invite_visit_some>(self, visitor: V) -> Result { + visitor.visit_some(self.inner) + } + + fn invite_deserialize>(self) -> Result { + T::deserialize(self.inner) + } } +/// A type implementing `Visitor` without any overrides. +/// +/// This allows for referring to the default serde implementation of +/// the `Visitor` methods from our own overrides. #[derive(Debug)] -pub struct DeserializerWithFds<'iter, Iter, De> { - pub deserializer: De, - pub fds: &'iter mut Iter, +struct VisitorDefaults<'a, V>(PhantomData<&'a V>); + +impl<'a, 'de, V> Visitor<'de> for VisitorDefaults<'a, V> { + type Value = V; + + fn expecting(&self, _: &mut Formatter) -> fmt::Result { + unreachable!() + } +} + +/// Like `SeqAccess`, but elements provide access to file descriptors +/// as well as data. +pub trait SeqAccessWithFds<'de> { + /// Like `SeqAccess::Error`. + type Error: Error; + + /// Like `SeqAccess::next_element_seed`, but `seed` is a + /// `DeserializeWithFdsSeed` instead of a `DeserializeSeed`. + fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> + where + T: DeserializeWithFdsSeed<'de>; + + /// Like `SeqAccess::next_element`, but returns a + /// `DeserializeWithFds` instead of a `Deserialize`. + fn next_element>(&mut self) -> Result, Self::Error> { + self.next_element_seed(PhantomData) + } + + /// See `SeqAccess::size_hint`. + fn size_hint(&self) -> Option { + None + } + + /// Invite a `Visitor` to visit. The inverse of `Visitor::visit`. + /// + /// The purpose of this indirection is to allow implementations to + /// control how `Visitor::visit_seq` is called. This means that + /// whatever way they get a `SeqAccess` to pass to it doesn't have + /// to be exposed publicly through the trait. + fn invite>(self, visitor: V) -> Result; +} + +impl<'de, 'fds, A, F> SeqAccessWithFds<'de> for WithFds<'fds, A, F> +where + A: SeqAccess<'de>, + F: Iterator, +{ + type Error = A::Error; + + fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> + where + T: DeserializeWithFdsSeed<'de>, + { + let wrapper = WithFds { + inner: seed, + fds: self.fds, + }; + + self.inner.next_element_seed(wrapper) + } + + fn size_hint(&self) -> Option { + self.inner.size_hint() + } + + fn invite>(self, visitor: V) -> Result { + visitor.visit_seq(self.inner) + } } -impl<'iter, Iter, De> DeserializerWithFds<'iter, Iter, De> { - pub fn new(fds: &'iter mut Iter, deserializer: De) -> Self { - Self { deserializer, fds } +/// Like `MapAccess`, but keys and values provide access to file +/// descriptors as well as data. +pub trait MapAccessWithFds<'de>: Sized { + /// Like `MapAccess::Error`. + type Error: Error; + + /// Like `MapAccess::next_key_seed`, but `seed` is a + /// `DeserializeWithFdsSeed` instead of a `DeserializeSeed`. + fn next_key_seed(&mut self, seed: K) -> Result, Self::Error> + where + K: DeserializeWithFdsSeed<'de>; + + /// Like `MapAccess::next_value_seed`, but `seed` is a + /// `DeserializeWithFdsSeed` instead of a `DeserializeSeed`. + fn next_value_seed(&mut self, seed: V) -> Result, Self::Error> + where + V: DeserializeWithFdsSeed<'de>; + + /// Like `MapAccess::next_entry_seed`, but `kseed` and `vseed` are + /// `DeserializeWithFdsSeed` instead of `DeserializeSeed`. + fn next_entry_seed, V: DeserializeWithFdsSeed<'de>>( + &mut self, + kseed: K, + vseed: V, + ) -> Result, Self::Error>; + + /// Like `MapAccess::next_key`, but returns a `DeserializeWithFds` + /// instead of a `Deserialize`. + fn next_key>(&mut self) -> Result, Self::Error> { + self.next_key_seed(PhantomData) + } + + /// Like `MapAccess::next_value`, but returns a `DeserializeWithFds` + /// instead of a `Deserialize`. + fn next_value>(&mut self) -> Result, Self::Error> { + self.next_value_seed(PhantomData) + } + + /// Like `MapAccess:next_entry`, but return values are + /// `DeserializeWithFds` instead of `Deserialize`. + fn next_entry(&mut self) -> Result, Self::Error> + where + K: DeserializeWithFds<'de>, + V: DeserializeWithFds<'de>, + { + self.next_entry_seed(PhantomData, PhantomData) + } + + /// Like `MapAccess::size_hint`. + fn size_hint(&self) -> Option { + None + } + + /// Invite a `Visitor` to visit. The inverse of `Visitor::visit`. + /// + /// The purpose of this indirection is to allow implementations to + /// control how `Visitor::visit_map` is called. This means that + /// whatever way they get a `MapAccess` to pass to it doesn't have + /// to be exposed publicly through the trait. + fn invite>(self, visitor: V) -> Result; +} + +/// Like `EnumAccess`, but variants provide access to file descriptors +/// as well as data. +pub trait EnumAccessWithFds<'de>: Sized { + /// Like `EnumAccess::Error`. + type Error: Error; + + /// Like `EnumAccess::Variant`, but also provides access to file + /// descriptors. + type Variant: VariantAccessWithFds<'de, Error = Self::Error>; + + /// Like `EnumAccess::variant_seed`, but `seed` is a + /// `DeserializeWithFdsSeed` instead of a `DeserializeSeed`. + fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error> + where + V: DeserializeSeed<'de>; + + /// Like `EnumAccess::variant`, but returns a `DeserializeWithFds` + /// instead of a `Deserialize`. + fn variant(self) -> Result<(V, Self::Variant), Self::Error> + where + V: Deserialize<'de>, + { + self.variant_seed(PhantomData) + } + + /// Invite a `Visitor` to visit. The inverse of `Visitor::visit`. + /// + /// The purpose of this indirection is to allow implementations to + /// control how `Visitor::visit_enum` is called. This means that + /// whatever way they get an `EnumAccess` to pass to it doesn't + /// have to be exposed publicly through the trait. + fn invite>(self, visitor: V) -> Result; +} + +impl<'fds, 'de, A, F> EnumAccessWithFds<'de> for WithFds<'fds, A, F> +where + A: EnumAccess<'de>, + F: Iterator, +{ + type Error = A::Error; + type Variant = WithFds<'fds, A::Variant, F>; + + fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error> + where + V: DeserializeSeed<'de>, + { + let (value, variant) = self.inner.variant_seed(seed)?; + let variant_with_fds = WithFds { + inner: variant, + fds: self.fds, + }; + + Ok((value, variant_with_fds)) + } + + fn invite>(self, visitor: V) -> Result { + visitor.visit_enum(self.inner) + } +} + +/// Like `VariantAccess`, but provides file descriptors as well as data. +pub trait VariantAccessWithFds<'de>: Sized { + /// Like `VariantAccess::Error`. + type Error: Error; + + /// Like `VariantAccess::unit_variant`. + fn unit_variant(self) -> Result<(), Self::Error>; + + /// Like `VariantAccess::newtype_variant_seed`, but `seed` is a + /// `DeserializeWithFdsSeed` instead of a `DeserializeSeed`. + fn newtype_variant_seed(self, seed: T) -> Result + where + T: DeserializeWithFdsSeed<'de>; + + /// Like `VariantAccess::newtype_variant`, but returns a + /// `DeserializeWithFds` instead of a `Deserialize`. + fn newtype_variant>(self) -> Result { + self.newtype_variant_seed(PhantomData) + } + + /// Like `VariantAccess::struct_variant`, but returns a + /// `DeserializeWithFds` instead of a `Deserialize`. + fn struct_variant>( + self, + fields: &'static [&'static str], + visitor: V, + ) -> Result; +} + +impl<'de, 'fds, A, F> VariantAccessWithFds<'de> for WithFds<'fds, A, F> +where + A: VariantAccess<'de>, + F: Iterator, +{ + type Error = A::Error; + + fn newtype_variant_seed(self, seed: T) -> Result + where + T: DeserializeWithFdsSeed<'de>, + { + let wrapper = WithFds { + inner: seed, + fds: self.fds, + }; + + self.inner.newtype_variant_seed(wrapper) + } + + fn unit_variant(self) -> Result<(), Self::Error> { + self.inner.unit_variant() + } + + fn struct_variant>( + self, + fields: &'static [&'static str], + visitor: V, + ) -> Result { + let wrapper = WithFds { + inner: visitor, + fds: self.fds, + }; + + self.inner.struct_variant(fields, wrapper) + } +} + +/// Like `Visitor`, but provides file descriptors as well as data. +pub trait VisitorWithFds<'de>: Sized { + /// Like `Visitor::Value`. + type Value; + + /// Like `Visitor::expecting`. + fn expecting(&self, f: &mut Formatter) -> fmt::Result; + + /// Like `Visitor::visit_none`. + fn visit_none(self) -> Result { + VisitorDefaults(PhantomData).visit_none() + } + + /// Like `Visitor:::visit_some`, but `deserializer` is a + /// `DeserializerWithFds` instead of a `Deserializer`. + fn visit_some(self, deserializer: D) -> Result + where + D: DeserializerWithFds<'de>, + { + deserializer.invite_visit_some(VisitorDefaults(PhantomData)) + } + + /// Like `Visitor::visit_seq`, but `seq` is a `SeqAccessWithFds` + /// instead of a `SeqAccess`. + fn visit_seq>(self, seq: A) -> Result { + seq.invite(VisitorDefaults(PhantomData)) + } + + /// Like `Visitor::visit_map`, but `map` is a `MapAccessWithFds` + /// instead of a `MapAccess`. + fn visit_map>(self, map: A) -> Result { + map.invite(VisitorDefaults(PhantomData)) + } + + /// Like `Visitor::visit_enum`, but `data` is an + /// `EnumAccessWithFds` instead of an `EnumAccess`. + fn visit_enum>(self, data: A) -> Result { + data.invite(VisitorDefaults(PhantomData)) + } +} + +impl<'de, 'fds, V, F> Visitor<'de> for WithFds<'fds, V, F> +where + V: VisitorWithFds<'de>, + F: Iterator, +{ + type Value = V::Value; + + fn expecting(&self, f: &mut Formatter) -> fmt::Result { + self.inner.expecting(f) + } + + fn visit_seq>(self, data: A) -> Result { + self.inner.visit_seq(WithFds { + inner: data, + fds: self.fds, + }) + } + + fn visit_enum>(self, data: A) -> Result { + self.inner.visit_enum(WithFds { + inner: data, + fds: self.fds, + }) + } +} + +macro_rules! deserialize_impl { + ($type:ty) => { + impl<'de> DeserializeWithFds<'de> for $type { + fn deserialize(deserializer: D) -> Result + where + D: DeserializerWithFds<'de>, + { + deserializer.invite_deserialize() + } + } + }; +} + +macro_rules! fd_impl { + ($type:ty) => { + impl<'de> DeserializeWithFds<'de> for $type { + fn deserialize>(deserializer: D) -> Result { + deserializer.deserialize_fd().map(|x| x.specialize()) + } + } + }; +} + +deserialize_impl!(()); + +deserialize_impl!(u8); +deserialize_impl!(u16); +deserialize_impl!(u32); +deserialize_impl!(u64); +deserialize_impl!(usize); + +deserialize_impl!(String); +deserialize_impl!(std::path::PathBuf); + +fd_impl!(std::fs::File); +fd_impl!(std::net::TcpListener); +fd_impl!(std::net::TcpStream); +fd_impl!(std::net::UdpSocket); +fd_impl!(std::os::unix::net::UnixDatagram); +fd_impl!(std::os::unix::net::UnixListener); +fd_impl!(std::os::unix::net::UnixStream); +fd_impl!(sys_util::net::UnixSeqpacket); + +impl<'de, T: DeserializeWithFds<'de>> DeserializeWithFds<'de> for Option { + fn deserialize>(deserializer: D) -> Result { + struct Visitor(PhantomData); + + impl<'de, T: DeserializeWithFds<'de>> VisitorWithFds<'de> for Visitor { + type Value = Option; + + fn expecting(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "option") + } + + fn visit_none(self) -> Result { + Ok(None) + } + + fn visit_some(self, deserializer: D) -> Result + where + D: DeserializerWithFds<'de>, + { + T::deserialize(deserializer).map(Some) + } + } + + deserializer.deserialize_option(Visitor(PhantomData)) + } +} + +impl<'de, T: DeserializeWithFds<'de>> DeserializeWithFds<'de> for Vec { + fn deserialize>(deserializer: D) -> Result { + struct Visitor(PhantomData); + + impl<'de, T: DeserializeWithFds<'de>> VisitorWithFds<'de> for Visitor { + type Value = Vec; + + fn expecting(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "a sequence") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccessWithFds<'de>, + { + let mut values = Vec::with_capacity(min(seq.size_hint().unwrap_or(0), 4096)); + + while let Some(value) = seq.next_element()? { + values.push(value); + } + + Ok(values) + } + } + + deserializer.deserialize_seq(Visitor(PhantomData)) + } +} + +impl<'de, K, V> DeserializeWithFds<'de> for BTreeMap +where + K: DeserializeWithFds<'de> + Ord, + V: DeserializeWithFds<'de>, +{ + fn deserialize>(deserializer: D) -> Result { + struct Visitor(PhantomData<(K, V)>); + + impl<'de, K, V> VisitorWithFds<'de> for Visitor + where + K: DeserializeWithFds<'de> + Ord, + V: DeserializeWithFds<'de>, + { + type Value = BTreeMap; + + fn expecting(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "a map") + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccessWithFds<'de>, + { + let mut values = BTreeMap::new(); + + while let Some((key, value)) = map.next_entry()? { + values.insert(key, value); + } + + Ok(values) + } + } + + deserializer.deserialize_map(Visitor(PhantomData)) } } diff --git a/msg_socket2/src/error.rs b/msg_socket2/src/error.rs index 2daa450..3aa0dd5 100644 --- a/msg_socket2/src/error.rs +++ b/msg_socket2/src/error.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright 2020, Alyssa Ross + use std::fmt::{self, Display, Formatter}; #[derive(Debug)] diff --git a/msg_socket2/src/fd.rs b/msg_socket2/src/fd.rs new file mode 100644 index 0000000..648be3e --- /dev/null +++ b/msg_socket2/src/fd.rs @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright 2020, Alyssa Ross + +use libc::close; +use std::mem::forget; +use std::os::unix::prelude::*; + +/// A type representing ownership of a file descriptor. +pub struct Fd(RawFd); + +impl Fd { + /// Construct an owned `Fd` from a raw file descriptor. + /// + /// This function is unsafe because `Fd` must be the sole owner of + /// the file descriptor, so that it can be closed when the `Fd` is + /// dropped. + pub unsafe fn new(fd: RawFd) -> Self { + Self(fd) + } + + /// Converts this `Fd` into a different implementation of + /// `FromRawFd` containing the same file descriptor. + /// + /// No checks that the file descriptor is of the appropriate type + /// are performed. The file descriptor will remain open. + pub fn specialize(self) -> T { + let fd = self.into_raw_fd(); + + // Safe because we transfer ownership of the file descriptor. + unsafe { T::from_raw_fd(fd) } + } +} + +impl IntoRawFd for Fd { + fn into_raw_fd(self) -> RawFd { + let fd = self.0; + forget(self); + fd + } +} + +impl AsRawFd for Fd { + fn as_raw_fd(&self) -> RawFd { + self.0 + } +} + +impl FromRawFd for Fd { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + Self(fd) + } +} + +impl Drop for Fd { + fn drop(&mut self) { + // Safe because we have sole ownership of fd. + unsafe { close(self.0) }; + } +} diff --git a/msg_socket2/src/lib.rs b/msg_socket2/src/lib.rs index 9bb2526..00500bb 100644 --- a/msg_socket2/src/lib.rs +++ b/msg_socket2/src/lib.rs @@ -1,36 +1,39 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 // Copyright 2020, Alyssa Ross -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -mod de; +//! A library for sending messages over Unix domain sockets, including +//! file descriptors. +//! +//! The serialization and deserialization interface is a wrapper on +//! top of bincode and serde. `Serializer` and `Deserializer` are +//! very large interfaces, so they have so far only been implemented +//! on-demand. But, implementing other methods as required should be +//! extremely straightforward to do based on the methods that have +//! already been implemented. +//! +//! In addition to serialization and deserialization, the library +//! provides a `Socket` type. This is a wrapper around a Unix +//! SOCK_SEQPACKET socket that automatically serializes outgoing +//! messages, and deserializes incoming ones. This might be changed +//! to `UnixStream`, to remove the dependency on `sys_util`. + +// TODO: document macros. + +pub mod de; mod error; +mod fd; pub mod ser; mod socket; pub(crate) use ser::FdSerializerImpl; -pub use de::{DeserializeWithFds, DeserializerWithFds}; +pub use de::{DeserializeWithFds, Deserializer, DeserializerWithFds}; pub use error::Error; -pub use ser::{FdSerializer, Serialize, SerializeWithFds, Serializer}; +use fd::Fd; +pub use msg_socket2_derive::{DeserializeWithFds, SerializeWithFds}; +pub use ser::{FdSerializer, SerializeWithFds, Serializer}; pub use socket::Socket; + +// Don't use ser and de re-exports, because they import from +// serde::ser and serde::de, so won't pick up derives. +pub use serde::{Deserialize, Serialize}; diff --git a/msg_socket2/src/ser.rs b/msg_socket2/src/ser.rs index e2b9900..07ead80 100644 --- a/msg_socket2/src/ser.rs +++ b/msg_socket2/src/ser.rs @@ -1,45 +1,22 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 // Copyright 2020, Alyssa Ross -// All rights reserved. // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// * Neither the name of the nor the -// names of its contributors may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Portions Copyright Erick Tryzelaar and David Tolnay, +// Portions Copyright Erick Tryzelaar and David Tolnay. // Licensed under either of Apache License, Version 2.0 // or MIT license at your option. //! Data structure serialization framework for Unix domain sockets. //! -//! Much like serde::ser, except sending file descriptors is also +//! Much like `serde::ser`, except sending file descriptors is also //! supported. -use serde::ser::*; use std::collections::BTreeMap; use std::fmt::{self, Display, Formatter}; use std::os::unix::prelude::*; pub use serde::ser::{ - Serialize, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTupleStruct, - SerializeTupleVariant, Serializer, + Error, Serialize, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, + SerializeTupleStruct, SerializeTupleVariant, Serializer, StdError, }; #[derive(Debug)] @@ -64,15 +41,21 @@ pub(crate) struct Composite<'ser, S> { serializer: &'ser mut S, } -/// Returned from `SerializerWithFds::serialize_seq`. +/// Like `SerializeSeq`, but accepts file descriptors as well as data. pub trait SerializeSeqFds { + /// Like `SerializeSeq::Ok`. type Ok; + + /// Like `SerializeSeq::Error`. type Error: Error; + /// Like `SerializeSeq::serialize_element`, but `value` is a + /// `SerializeWithFds` instead of a `Serialize`. fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> where T: SerializeWithFds + ?Sized; + /// Like `SerializeSeq::end`. fn end(self) -> Result; } @@ -93,15 +76,21 @@ impl<'ser, 'fds> SerializeSeqFds for Composite<'ser, FdSerializerImpl<'fds>> { } } -/// Returned from `SerializerWithFds::serialize_tuple_struct`. +/// Like `SerializeTupleStruct`, but accepts file descriptors as well as data. pub trait SerializeTupleStructFds { + /// Like `SerializeTupleStruct::Ok`. type Ok; + + /// Like `SerializeTupleStruct::Error`. type Error: Error; + /// Like `SerializeTupleStruct::serialize_field`, but `value` is a + /// `SerializeWithFds` instead of a `Serialize`. fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> where T: SerializeWithFds + ?Sized; + /// Like `SerializeTupleStruct::end`. fn end(self) -> Result; } @@ -122,15 +111,22 @@ impl<'ser, 'fds> SerializeTupleStructFds for Composite<'ser, FdSerializerImpl<'f } } -/// Returned from `SerializerWithFds::serialize_tuple_variant`. +/// Like `SerializeTupleVariant`, but accepts file descriptors as well +/// as data. pub trait SerializeTupleVariantFds { + /// Like `SerializeTupleVariant::Ok`. type Ok; + + /// Like `SerializeTupleVariant::Error`. type Error: Error; + /// Like `SerializeTupleVariant::serialize_field`, but `value` is + /// a `SerializeWithFds` instead of a `Serialize`. fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> where T: SerializeWithFds + ?Sized; + /// Like `SerializeTupleVariant::end`. fn end(self) -> Result; } @@ -151,17 +147,24 @@ impl<'ser, 'fds> SerializeTupleVariantFds for Composite<'ser, FdSerializerImpl<' } } -/// Returned from `SerializerWithFds::serialize_map`. +/// Like `SerializeMap`, but provides file descriptors as well as data. pub trait SerializeMapFds { + /// Like `SerializeMap::Ok`. type Ok; + + /// Like `SerializeMap::Error`. type Error: Error; + /// Like `SerializeMap::serialize_key`, but `key` is a `SerializeWithFds` instead of a `Serialize`. fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error>; + /// Like `SerializeMap::serialize_value`, but `value` is a `SerializeWithFds` instead of a `Serialize`. fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> where T: SerializeWithFds + ?Sized; + /// Like `SerializeMap::serialize_key`, but `key` and `value` are + /// `SerializeWithFds` instead of `Serialize`. fn serialize_entry( &mut self, key: &K, @@ -171,6 +174,7 @@ pub trait SerializeMapFds { self.serialize_value(value) } + /// Like `SerializeMap::end`. fn end(self) -> Result; } @@ -196,20 +200,28 @@ impl<'ser, 'fds> SerializeMapFds for Composite<'ser, FdSerializerImpl<'fds>> { } } -/// Returned from `SerializerWithFds::serialize_struct`. +/// Like `SerializeStruct`, but provides file descriptors as well as +/// data. pub trait SerializeStructFds { + /// Like `SerializeStruct::Ok`. type Ok; + + /// Like `SerializeStruct::Error`. type Error: Error; + /// Like `SerializeStruct::serialize_field`, but `value` is a + /// `SerializeWithFds` instead of a `Serialize`. fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> where T: SerializeWithFds + ?Sized; + /// Like `SerializeStruct::skip_field`. fn skip_field(&mut self, key: &'static str) -> Result<(), Self::Error> { let _ = key; Ok(()) } + /// Like `SerializeStruct::end`. fn end(self) -> Result; } @@ -230,20 +242,28 @@ impl<'ser, 'fds> SerializeStructFds for Composite<'ser, FdSerializerImpl<'fds>> } } -/// Returned from `SerializerWithFds::serialize_struct_variant`. +/// Like `SerializeStructVariant`, but provides file descriptors as +/// well as data. pub trait SerializeStructVariantFds { + /// Like `SerializeStructVariant::Ok`. type Ok; + + /// Like `SerializeStructVariant::Error`. type Error: Error; + /// Like `SerializeStructVariant::serialize_field`, but `key` and + /// `value` are `SerializeWithFds` instead of `Serialize`. fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> where T: SerializeWithFds + ?Sized; + /// Like `SerializeStructVariant::skip_field`. fn skip_field(&mut self, key: &'static str) -> Result<(), Self::Error> { let _ = key; Ok(()) } + /// Like `SerializeStructVariant::end`. fn end(self) -> Result; } @@ -264,28 +284,54 @@ impl<'ser, 'fds> SerializeStructVariantFds for Composite<'ser, FdSerializerImpl< } } -/// A **data format** that can send file descriptors between Unix processes. +/// Like `Serializer`, but accepts file descriptors instead of data. pub trait FdSerializer: Sized { + /// Like `Serializer::Ok`. type Ok; + + /// Like `Serializer::Error`. type Error: Error; + /// Like `Serializer::SerializeSeq`, but serializes + /// `SerializeWithFds` instead of `Serialize`. type SerializeSeqFds: SerializeSeqFds; + + /// Like `Serializer::SerializeTupleStruct`, but serializes + /// `SerializeWithFds` instead of `Serialize`. type SerializeTupleStructFds: SerializeTupleStructFds; + + /// Like `Serializer::SerializeTupleVariant`, but serializes + /// `SerializeWithFds` instead of `Serialize`. type SerializeTupleVariantFds: SerializeTupleVariantFds; + + /// Like `Serializer::SerializeMap`, but serializes + /// `SerializeWithFds` instead of `Serialize`. type SerializeMapFds: SerializeMapFds; + + /// Like `Serializer::SerializeStruct`, but serializes + /// `SerializeWithFds` instead of `Serialize`. type SerializeStructFds: SerializeStructFds; + + /// Like `Serializer::SerializeStructVariant`, but serializes + /// `SerializeWithFds` instead of `Serialize`. type SerializeStructVariantFds: SerializeStructVariantFds; + /// Serialize a file descriptor. fn serialize_raw_fd(self, value: RawFd) -> Result; + /// Like `Serializer::serialize_none`. fn serialize_none(self) -> Result; + /// Like `Serializer::serialize_some`, but `value` is a + /// `SerializeWithFds` instead of a `Serialize`. fn serialize_some(self, value: &T) -> Result where T: SerializeWithFds + ?Sized; + /// Like `Serializer::serialize_unit`. fn serialize_unit(self) -> Result; + /// Like `Serializer::serialize_unit_variant`. fn serialize_unit_variant( self, name: &'static str, @@ -293,14 +339,23 @@ pub trait FdSerializer: Sized { variant: &'static str, ) -> Result; + /// Like `Serializer::serialize_seq`, but returns a + /// `SerializeSeqFds`, which serializes file descriptors as well + /// as data. fn serialize_seq(self, len: Option) -> Result; + /// Like `Serializer::serialize_tuple_struct`, but returns a + /// `SerializeTupleStructFds`, which serializes file descriptors as well + /// as data. fn serialize_tuple_struct( self, name: &'static str, len: usize, ) -> Result; + /// Like `Serializer::serialize_tuple_variant`, but returns a + /// `SerializeTupleVariantFds`, which serializes file descriptors + /// as well as data. fn serialize_tuple_variant( self, name: &'static str, @@ -309,14 +364,23 @@ pub trait FdSerializer: Sized { len: usize, ) -> Result; + /// Like `Serializer::serialize_map`, but returns a + /// `SerializeMapFds`, which serializes file descriptors as well + /// as data. fn serialize_map(self, len: Option) -> Result; + /// Like `Serializer::serialize_struct`, but returns a + /// `SerializeStructFds`, which serializes file descriptors as + /// well as data. fn serialize_struct( self, name: &'static str, len: usize, ) -> Result; + /// Like `Serializer::serialize_struct_variant`, but returns a + /// `SerializeStructVariantFds`, which serializes file descriptors + /// as well as data. fn serialize_struct_variant( self, name: &'static str, @@ -416,10 +480,16 @@ impl<'ser, 'fds> FdSerializer for &'ser mut FdSerializerImpl<'fds> { } } -/// A **data structure** that can be sent over a Unix domain socket. +/// Like `Serialize`, but additionally provides the `serialize_fds` method. pub trait SerializeWithFds { - fn serialize(&self, serializer: Ser) -> Result; - fn serialize_fds(&self, serializer: Ser) -> Result; + /// Like `Serialize::serialize`. + fn serialize(&self, serializer: S) -> Result; + + /// Serialize all file descriptors associated with this value, recursively. + /// + /// In most cases, the implementation should largely mirror the + /// `serialize` implementation. + fn serialize_fds(&self, serializer: S) -> Result; } macro_rules! serialize_impl { @@ -489,6 +559,7 @@ fd_impl!(std::os::unix::net::UnixStream); fd_impl!(std::process::ChildStderr); fd_impl!(std::process::ChildStdin); fd_impl!(std::process::ChildStdout); +fd_impl!(sys_util::net::UnixSeqpacket); impl SerializeWithFds for Option { fn serialize(&self, serializer: Ser) -> Result { diff --git a/msg_socket2/src/socket.rs b/msg_socket2/src/socket.rs index b836365..bd1bc00 100644 --- a/msg_socket2/src/socket.rs +++ b/msg_socket2/src/socket.rs @@ -1,11 +1,16 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright 2020, Alyssa Ross + use bincode::{DefaultOptions, Deserializer, Serializer}; use std::io::IoSlice; use std::marker::PhantomData; use std::os::unix::prelude::*; use sys_util::{net::UnixSeqpacket, ScmSocket}; -use crate::{DeserializeWithFds, DeserializerWithFds, Error, FdSerializerImpl, SerializeWithFds}; +use crate::{de, DeserializeWithFds, Error, Fd, FdSerializerImpl, SerializeWithFds}; +/// A Unix **SOCK_SEQPACKET** socket that can send and receive values +/// consisting of binary data and file descriptors. #[derive(Debug)] pub struct Socket { sock: UnixSeqpacket, @@ -39,11 +44,16 @@ impl Socket { impl DeserializeWithFds<'de>> Socket { pub fn recv(&self) -> Result { - let (bytes, fds) = self.sock.recv_as_vec_with_fds()?; - let mut fds_iter = fds.into_iter(); + let (bytes, raw_fds) = self.sock.recv_as_vec_with_fds()?; + + // Safe because we just received ownership of the fds. + let mut fds = raw_fds.into_iter().map(|fd| unsafe { Fd::new(fd) }); let mut deserializer = Deserializer::from_slice(&bytes, DefaultOptions::new()); - let deserializer_with_fds = DeserializerWithFds::new(&mut fds_iter, &mut deserializer); + let deserializer_with_fds = de::WithFds { + inner: &mut deserializer, + fds: &mut fds, + }; Ok(Recv::deserialize(deserializer_with_fds)?) } -- cgit 1.4.1