use std::os::fd::{OwnedFd, BorrowedFd};
use super::FD_SIZE;
#[derive(Debug)]
pub struct AncillaryMessageReader<'a> {
pub(crate) buffer: &'a mut [u8],
pub(crate) truncated: bool,
}
#[derive(Copy, Clone)]
pub struct AncillaryMessages<'a> {
buffer: &'a [u8],
current: Option<&'a libc::cmsghdr>,
}
pub struct IntoAncillaryMessages<'a> {
buffer: &'a mut [u8],
current: Option<&'a libc::cmsghdr>,
}
pub enum AncillaryMessage<'a> {
FileDescriptors(FileDescriptors<'a>),
#[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "netbsd",))]
Credentials(UnixCredentials<'a>),
Other(UnknownMessage<'a>)
}
pub enum OwnedAncillaryMessage<'a> {
FileDescriptors(OwnedFileDescriptors<'a>),
#[cfg(any(doc, target_os = "android", target_os = "linux", target_os = "netbsd",))]
Credentials(UnixCredentials<'a>),
Other(UnknownMessage<'a>)
}
#[derive(Copy, Clone)]
pub struct FileDescriptors<'a> {
data: &'a [u8],
}
pub struct OwnedFileDescriptors<'a> {
data: &'a mut [u8],
}
#[derive(Copy, Clone)]
#[cfg(any(doc, target_os = "linux", target_os = "android", target_os = "netbsd"))]
pub struct UnixCredentials<'a> {
data: &'a [u8],
}
#[derive(Copy, Clone)]
pub struct UnknownMessage<'a> {
cmsg_level: i32,
cmsg_type: i32,
data: &'a [u8],
}
impl<'a> AncillaryMessageReader<'a> {
pub unsafe fn new(buffer: &'a mut [u8], truncated: bool) -> Self {
Self { buffer, truncated }
}
pub fn len(&self) -> usize {
self.buffer.len()
}
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
pub fn is_truncated(&self) -> bool {
self.truncated
}
pub fn messages(&self) -> AncillaryMessages<'_> {
AncillaryMessages { buffer: self.buffer, current: None }
}
pub fn into_messages(mut self) -> IntoAncillaryMessages<'a> {
let buffer = std::mem::take(&mut self.buffer);
IntoAncillaryMessages { buffer, current: None }
}
}
impl Drop for AncillaryMessageReader<'_> {
fn drop(&mut self) {
if !self.is_empty() {
drop(IntoAncillaryMessages { buffer: self.buffer, current: None })
}
}
}
impl<'a> Iterator for AncillaryMessages<'a> {
type Item = AncillaryMessage<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.buffer.is_empty() {
return None;
}
unsafe {
let mut msg: libc::msghdr = std::mem::zeroed();
msg.msg_control = self.buffer.as_ptr() as *mut _;
msg.msg_controllen = self.buffer.len() as _;
let cmsg = if let Some(current) = self.current {
libc::CMSG_NXTHDR(&msg, current)
} else {
libc::CMSG_FIRSTHDR(&msg)
};
let cmsg = cmsg.as_ref()?;
if let Some(current) = self.current {
if std::ptr::eq(current, cmsg) {
return None;
}
}
self.current = Some(cmsg);
let ancillary_result = AncillaryMessage::try_from_cmsghdr(cmsg);
Some(ancillary_result)
}
}
}
impl<'a> AncillaryMessage<'a> {
#[allow(clippy::unnecessary_cast)]
fn try_from_cmsghdr(cmsg: &'a libc::cmsghdr) -> Self {
unsafe {
let cmsg_len_zero = libc::CMSG_LEN(0) as usize;
let data_len = cmsg.cmsg_len as usize - cmsg_len_zero;
let data = libc::CMSG_DATA(cmsg).cast();
let data = std::slice::from_raw_parts(data, data_len);
match (cmsg.cmsg_level, cmsg.cmsg_type) {
(libc::SOL_SOCKET, libc::SCM_RIGHTS) => Self::FileDescriptors(FileDescriptors { data }),
#[cfg(any(target_os = "android", target_os = "linux", target_os = "netbsd"))]
(libc::SOL_SOCKET, super::SCM_CREDENTIALS) => Self::Credentials(UnixCredentials { data }),
(cmsg_level, cmsg_type) => Self::Other(UnknownMessage { cmsg_level, cmsg_type, data }),
}
}
}
}
impl<'a> Iterator for IntoAncillaryMessages<'a> {
type Item = OwnedAncillaryMessage<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.buffer.is_empty() {
return None;
}
unsafe {
let mut msg: libc::msghdr = std::mem::zeroed();
msg.msg_control = self.buffer.as_ptr() as *mut _;
msg.msg_controllen = self.buffer.len() as _;
let cmsg = if let Some(current) = self.current {
libc::CMSG_NXTHDR(&msg, current)
} else {
libc::CMSG_FIRSTHDR(&msg)
};
let cmsg = cmsg.as_ref()?;
if let Some(current) = self.current {
if std::ptr::eq(current, cmsg) {
return None;
}
}
self.current = Some(cmsg);
let ancillary_result = OwnedAncillaryMessage::try_from_cmsghdr(cmsg);
Some(ancillary_result)
}
}
}
impl Drop for IntoAncillaryMessages<'_> {
fn drop(&mut self) {
for message in self {
drop(message)
}
}
}
impl<'a> OwnedAncillaryMessage<'a> {
#[allow(clippy::unnecessary_cast)]
fn try_from_cmsghdr(cmsg: &'a libc::cmsghdr) -> Self {
unsafe {
let cmsg_len_zero = libc::CMSG_LEN(0) as usize;
let data_len = cmsg.cmsg_len as usize - cmsg_len_zero;
let data = libc::CMSG_DATA(cmsg).cast();
let data = std::slice::from_raw_parts_mut(data, data_len);
match (cmsg.cmsg_level, cmsg.cmsg_type) {
(libc::SOL_SOCKET, libc::SCM_RIGHTS) => Self::FileDescriptors(OwnedFileDescriptors { data }),
#[cfg(any(target_os = "android", target_os = "linux", target_os = "netbsd"))]
(libc::SOL_SOCKET, super::SCM_CREDENTIALS) => Self::Credentials(UnixCredentials { data }),
(cmsg_level, cmsg_type) => Self::Other(UnknownMessage { cmsg_level, cmsg_type, data }),
}
}
}
}
impl<'a> FileDescriptors<'a> {
pub fn len(&self) -> usize {
self.data.len() / FD_SIZE
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get(&self, index: usize) -> Option<BorrowedFd<'a>> {
if index >= self.len() {
None
} else {
unsafe {
Some(std::ptr::read_unaligned(self.data[index * FD_SIZE..].as_ptr().cast()))
}
}
}
}
impl<'a> Iterator for FileDescriptors<'a> {
type Item = BorrowedFd<'a>;
fn next(&mut self) -> Option<Self::Item> {
let fd = self.get(0)?;
self.data = &self.data[FD_SIZE..];
Some(fd)
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len(), Some(self.len()))
}
}
impl<'a> std::iter::ExactSizeIterator for FileDescriptors<'a> {
fn len(&self) -> usize {
self.len()
}
}
impl<'a> OwnedFileDescriptors<'a> {
pub fn len(&self) -> usize {
self.data.len() / FD_SIZE
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
fn advance(&mut self) {
let data = std::mem::take(&mut self.data);
self.data = &mut data[FD_SIZE..];
}
}
impl<'a> Iterator for OwnedFileDescriptors<'a> {
type Item = OwnedFd;
fn next(&mut self) -> Option<Self::Item> {
if Self::is_empty(self) {
None
} else {
unsafe {
use std::os::fd::{FromRawFd, RawFd};
let raw_fd: RawFd = std::ptr::read_unaligned(self.data.as_mut_ptr().cast());
self.advance();
Some(OwnedFd::from_raw_fd(raw_fd))
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len(), Some(self.len()))
}
}
impl Drop for OwnedFileDescriptors<'_> {
fn drop(&mut self) {
for fd in self {
drop(fd)
}
}
}
impl<'a> std::iter::ExactSizeIterator for OwnedFileDescriptors<'a> {
fn len(&self) -> usize {
self.len()
}
}
#[cfg(any(target_os = "linux", target_os = "android", target_os = "netbsd"))]
mod unix_creds_impl {
use super::UnixCredentials;
use super::super::RawScmCreds;
use crate::UCred;
impl UnixCredentials<'_> {
pub fn len(&self) -> usize {
self.data.len() / std::mem::size_of::<RawScmCreds>()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get(&self, index: usize) -> Option<UCred> {
if index >= self.len() {
None
} else {
let raw: RawScmCreds = unsafe {
std::ptr::read_unaligned(self.data.as_ptr().cast::<RawScmCreds>().add(index))
};
Some(UCred::from_scm_creds(raw))
}
}
}
impl Iterator for UnixCredentials<'_> {
type Item = UCred;
fn next(&mut self) -> Option<Self::Item> {
let fd = self.get(0)?;
self.data = &self.data[std::mem::size_of::<RawScmCreds>()..];
Some(fd)
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len(), Some(self.len()))
}
}
impl<'a> std::iter::ExactSizeIterator for UnixCredentials<'a> {
fn len(&self) -> usize {
self.len()
}
}
}
impl<'a> UnknownMessage<'a> {
pub fn cmsg_level(&self) -> i32 {
self.cmsg_level
}
pub fn cmsg_type(&self) -> i32 {
self.cmsg_type
}
pub fn data(&self) -> &'a [u8] {
self.data
}
}