use super::imports::*;
use cfg_if::cfg_if;
use std::{
borrow::Cow,
iter::{FromIterator, FusedIterator},
mem::size_of,
};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum AncillaryData<'a> {
FileDescriptors(Cow<'a, [c_int]>),
#[cfg(any(doc, uds_ucred))]
#[cfg_attr( // uds_ucred template
feature = "doc_cfg",
doc(cfg(any(
all(
target_os = "linux",
any(
target_env = "gnu",
target_env = "musl",
target_env = "musleabi",
target_env = "musleabihf"
)
),
target_os = "emscripten",
target_os = "redox"
)))
)]
Credentials {
pid: pid_t,
uid: uid_t,
gid: gid_t,
},
}
impl<'a> AncillaryData<'a> {
pub const ENCODED_SIZE_OF_CREDENTIALS: usize = Self::_ENCODED_SIZE_OF_CREDENTIALS;
cfg_if! {
if #[cfg(uds_ucred)] {
const _ENCODED_SIZE_OF_CREDENTIALS: usize = size_of::<cmsghdr>() + size_of::<ucred>();
} else if #[cfg(unix)] {
const _ENCODED_SIZE_OF_CREDENTIALS: usize = size_of::<cmsghdr>();
} else {
const _ENCODED_SIZE_OF_CREDENTIALS: usize = 0;
}
}
pub const fn encoded_size_of_file_descriptors(num_descriptors: usize) -> usize {
#[cfg(not(unix))]
struct cmsghdr; size_of::<cmsghdr>() + num_descriptors * size_of::<pid_t>()
}
#[must_use]
pub fn clone_ref(&'a self) -> Self {
match *self {
Self::FileDescriptors(ref fds) => Self::FileDescriptors(Cow::Borrowed(fds)),
#[cfg(uds_ucred)]
Self::Credentials { pid, uid, gid } => Self::Credentials { pid, uid, gid },
}
}
pub fn encoded_size(&self) -> usize {
match self {
Self::FileDescriptors(fds) => Self::encoded_size_of_file_descriptors(fds.len()),
#[cfg(uds_scm_credentials)]
Self::Credentials { .. } => Self::ENCODED_SIZE_OF_CREDENTIALS,
}
}
pub fn encode(op: impl IntoIterator<Item = Self>) -> EncodedAncillaryData<'static> {
let items = op.into_iter();
let mut buffer = Vec::with_capacity(
{
let size_hint = items.size_hint();
size_hint.1.unwrap_or(size_hint.0)
} * Self::ENCODED_SIZE_OF_CREDENTIALS,
);
for i in items {
let mut cmsg_len = size_of::<cmsghdr>();
let cmsg_level_bytes = SOL_SOCKET.to_ne_bytes();
let cmsg_type_bytes;
match i {
AncillaryData::FileDescriptors(fds) => {
cmsg_type_bytes = SCM_RIGHTS.to_ne_bytes();
cmsg_len += fds.len() * 4;
let cmsg_len_bytes = cmsg_len.to_ne_bytes();
buffer.extend_from_slice(&cmsg_len_bytes);
buffer.extend_from_slice(&cmsg_level_bytes);
buffer.extend_from_slice(&cmsg_type_bytes);
for i in fds.iter().copied() {
let desc_bytes = i.to_ne_bytes();
buffer.extend_from_slice(&desc_bytes);
}
}
#[cfg(uds_ucred)]
AncillaryData::Credentials { pid, uid, gid } => {
cmsg_type_bytes = SCM_RIGHTS.to_ne_bytes();
cmsg_len += size_of::<ucred>();
let cmsg_len_bytes = cmsg_len.to_ne_bytes();
let pid_bytes = pid.to_ne_bytes();
let uid_bytes = uid.to_ne_bytes();
let gid_bytes = gid.to_ne_bytes();
buffer.extend_from_slice(&cmsg_len_bytes);
buffer.extend_from_slice(&cmsg_level_bytes);
buffer.extend_from_slice(&cmsg_type_bytes);
buffer.extend_from_slice(&pid_bytes);
buffer.extend_from_slice(&uid_bytes);
buffer.extend_from_slice(&gid_bytes);
}
}
}
EncodedAncillaryData(Cow::Owned(buffer))
}
}
impl AncillaryData<'static> {
#[cfg(any(doc, uds_ucred))]
#[cfg_attr( // uds_ucred template
feature = "doc_cfg",
doc(cfg(any(
all(
target_os = "linux",
any(
target_env = "gnu",
target_env = "musl",
target_env = "musleabi",
target_env = "musleabihf"
)
),
target_os = "emscripten",
target_os = "redox"
)))
)]
pub fn credentials() -> Self {
Self::Credentials {
pid: unsafe { libc::getpid() },
uid: unsafe { libc::getuid() },
gid: unsafe { libc::getgid() },
}
}
}
#[repr(transparent)]
#[derive(Clone, Debug)]
pub struct EncodedAncillaryData<'a>(pub Cow<'a, [u8]>);
impl<'a> From<&'a [u8]> for EncodedAncillaryData<'a> {
fn from(op: &'a [u8]) -> Self {
Self(Cow::Borrowed(op))
}
}
impl From<Vec<u8>> for EncodedAncillaryData<'static> {
fn from(op: Vec<u8>) -> Self {
Self(Cow::Owned(op))
}
}
impl<'b> FromIterator<AncillaryData<'b>> for EncodedAncillaryData<'static> {
fn from_iter<I: IntoIterator<Item = AncillaryData<'b>>>(iter: I) -> Self {
AncillaryData::encode(iter)
}
}
impl<'b> From<Vec<AncillaryData<'b>>> for EncodedAncillaryData<'static> {
fn from(op: Vec<AncillaryData<'b>>) -> Self {
Self::from_iter(op)
}
}
impl<'b: 'c, 'c> From<&'c [AncillaryData<'b>]> for EncodedAncillaryData<'static> {
fn from(op: &'c [AncillaryData<'b>]) -> Self {
op.iter().map(AncillaryData::clone_ref).collect::<Self>()
}
}
impl<'a> AsRef<[u8]> for EncodedAncillaryData<'a> {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
#[derive(Debug)]
pub enum AncillaryDataBuf<'a> {
Borrowed(&'a mut [u8]),
Owned(Vec<u8>),
}
impl<'a> AncillaryDataBuf<'a> {
pub fn owned_with_capacity(capacity: usize) -> Self {
Self::Owned(Vec::with_capacity(capacity))
}
pub fn decode(&'a self) -> AncillaryDataDecoder<'a> {
AncillaryDataDecoder {
buffer: self.as_ref(),
i: 0,
}
}
}
impl<'a> From<&'a mut [u8]> for AncillaryDataBuf<'a> {
fn from(op: &'a mut [u8]) -> Self {
Self::Borrowed(op)
}
}
impl From<Vec<u8>> for AncillaryDataBuf<'static> {
fn from(op: Vec<u8>) -> Self {
Self::Owned(op)
}
}
impl<'a> From<&'a mut AncillaryDataBuf<'a>> for AncillaryDataBuf<'a> {
fn from(op: &'a mut AncillaryDataBuf<'a>) -> Self {
match op {
Self::Borrowed(slice) => Self::Borrowed(slice),
Self::Owned(vec) => Self::Borrowed(vec),
}
}
}
impl<'a> AsRef<[u8]> for AncillaryDataBuf<'a> {
fn as_ref(&self) -> &[u8] {
match self {
Self::Borrowed(slice) => slice,
Self::Owned(vec) => vec,
}
}
}
impl<'a> AsMut<[u8]> for AncillaryDataBuf<'a> {
fn as_mut(&mut self) -> &mut [u8] {
match self {
Self::Borrowed(slice) => slice,
Self::Owned(vec) => vec,
}
}
}
#[derive(Clone, Debug)]
pub struct AncillaryDataDecoder<'a> {
buffer: &'a [u8],
i: usize,
}
impl<'a> From<&'a AncillaryDataBuf<'a>> for AncillaryDataDecoder<'a> {
fn from(buffer: &'a AncillaryDataBuf<'a>) -> Self {
buffer.decode()
}
}
impl<'a> Iterator for AncillaryDataDecoder<'a> {
type Item = AncillaryData<'static>;
fn next(&mut self) -> Option<Self::Item> {
fn u32_from_slice(bytes: &[u8]) -> u32 {
u32::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
}
fn u64_from_slice(bytes: &[u8]) -> u64 {
u64::from_ne_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
])
}
let bytes = self.buffer;
let end = bytes.len() - 1;
if matches!(bytes.len().checked_sub(self.i), Some(0) | None) {
self.i = end;
return None;
}
#[cfg(target_pointer_width = "64")]
let element_size = {
if bytes.len() - self.i < 8 {
self.i = end;
return None;
}
u64_from_slice(&bytes[self.i..self.i + 8]) as usize
};
#[cfg(target_pointer_width = "32")]
let element_size = {
if bytes.len() - self.i < 4 {
self.i = end;
return None;
}
u32_from_slice(&bytes[self.i..self.i + 4]) as usize
};
#[cfg(target_pointer_width = "64")]
let type_offset: usize = 8 + 4; #[cfg(target_pointer_width = "32")]
let type_offset: usize = 4 + 4;
let element_type = u32_from_slice(&bytes[self.i + type_offset..=self.i + type_offset + 4]);
let element_offset = type_offset + 4;
self.i += element_offset + element_size;
match element_type as i32 {
SCM_RIGHTS => {
let amount_of_descriptors = element_size / 4;
let mut descriptors = Vec::<c_int>::with_capacity(amount_of_descriptors);
let mut descriptor_offset = element_offset;
for _ in 0..amount_of_descriptors {
descriptors.push(
u32_from_slice(&bytes[descriptor_offset..descriptor_offset + 4]) as i32,
);
descriptor_offset += 4;
}
Some(AncillaryData::FileDescriptors(Cow::Owned(descriptors)))
}
#[cfg(uds_ucred)]
SCM_CREDENTIALS => {
let pid_offset = element_offset;
let pid: pid_t = u32_from_slice(&bytes[pid_offset..pid_offset + 4]) as pid_t;
let uid_offset = pid_offset + 4;
let uid: uid_t = u32_from_slice(&bytes[uid_offset..uid_offset + 4]) as uid_t;
let gid_offset = uid_offset + 4;
let gid: gid_t = u32_from_slice(&bytes[gid_offset..gid_offset + 4]) as gid_t;
Some(AncillaryData::Credentials { pid, uid, gid })
}
_ => self.next(), }
}
}
impl FusedIterator for AncillaryDataDecoder<'_> {}