use std::io;
use std::os::unix::io::{AsFd, BorrowedFd, OwnedFd};
use std::os::unix::net::{UnixDatagram, UnixStream};
use crate::ancillary::{AncillaryData, SocketAncillary};
use crate::cmsg;
const DEFAULT_STREAM_BUF: usize = 4096;
const DEFAULT_DATAGRAM_BUF: usize = 65536;
const MAX_RECV_FDS: usize = 253;
#[derive(Debug)]
#[non_exhaustive]
pub struct ReceivedFds {
pub data: Vec<u8>,
pub fds: Vec<OwnedFd>,
}
fn send_fds_impl(fd: BorrowedFd<'_>, data: &[u8], fds: &[BorrowedFd<'_>]) -> io::Result<usize> {
let mut buf = vec![0u8; SocketAncillary::buffer_size_for_rights(fds.len())];
let mut ancillary = SocketAncillary::new(&mut buf);
ancillary
.add_fds(fds)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
let iov = [io::IoSlice::new(data)];
cmsg::sendmsg_vectored(fd, &iov, ancillary.buffer, ancillary.length)
}
fn recv_fds_into_impl<const N: usize>(
fd: BorrowedFd<'_>,
data_buf: &mut [u8],
) -> io::Result<(usize, Vec<OwnedFd>)> {
let cap = N.max(MAX_RECV_FDS);
let mut anc_buf = vec![0u8; SocketAncillary::buffer_size_for_rights(cap)];
let mut iov = [io::IoSliceMut::new(data_buf)];
let result = cmsg::recvmsg_vectored(fd, &mut iov, &mut anc_buf)?;
let ancillary = SocketAncillary {
buffer: &mut anc_buf,
length: result.ancillary_len,
truncated: result.truncated,
};
let mut all_fds: Vec<OwnedFd> = Vec::new();
for msg in ancillary.messages() {
match msg {
AncillaryData::ScmRights(rights) => all_fds.extend(rights),
}
}
if result.truncated {
drop(all_fds);
return Err(io::Error::other(
"ancillary truncated despite oversized buffer; possible fd leak — abort the connection",
));
}
let kept: Vec<OwnedFd> = all_fds.into_iter().take(N).collect();
Ok((result.bytes_read, kept))
}
pub trait UnixStreamExt {
fn send_fds(&self, data: &[u8], fds: &[impl AsFd]) -> io::Result<usize>;
fn recv_fds<const N: usize>(&self) -> io::Result<ReceivedFds>;
fn recv_fds_into<const N: usize>(
&self,
data_buf: &mut [u8],
) -> io::Result<(usize, Vec<OwnedFd>)>;
}
impl UnixStreamExt for UnixStream {
fn send_fds(&self, data: &[u8], fds: &[impl AsFd]) -> io::Result<usize> {
let borrowed: Vec<BorrowedFd<'_>> = fds.iter().map(|f| f.as_fd()).collect();
send_fds_impl(self.as_fd(), data, &borrowed)
}
fn recv_fds<const N: usize>(&self) -> io::Result<ReceivedFds> {
let mut data_buf = vec![0u8; DEFAULT_STREAM_BUF];
let (n, fds) = recv_fds_into_impl::<N>(self.as_fd(), &mut data_buf)?;
data_buf.truncate(n);
Ok(ReceivedFds {
data: data_buf,
fds,
})
}
fn recv_fds_into<const N: usize>(
&self,
data_buf: &mut [u8],
) -> io::Result<(usize, Vec<OwnedFd>)> {
recv_fds_into_impl::<N>(self.as_fd(), data_buf)
}
}
pub trait UnixDatagramExt {
fn send_fds(&self, data: &[u8], fds: &[impl AsFd]) -> io::Result<usize>;
fn recv_fds<const N: usize>(&self) -> io::Result<ReceivedFds>;
fn recv_fds_into<const N: usize>(
&self,
data_buf: &mut [u8],
) -> io::Result<(usize, Vec<OwnedFd>)>;
}
impl UnixDatagramExt for UnixDatagram {
fn send_fds(&self, data: &[u8], fds: &[impl AsFd]) -> io::Result<usize> {
let borrowed: Vec<BorrowedFd<'_>> = fds.iter().map(|f| f.as_fd()).collect();
send_fds_impl(self.as_fd(), data, &borrowed)
}
fn recv_fds<const N: usize>(&self) -> io::Result<ReceivedFds> {
let mut data_buf = vec![0u8; DEFAULT_DATAGRAM_BUF];
let (n, fds) = recv_fds_into_impl::<N>(self.as_fd(), &mut data_buf)?;
data_buf.truncate(n);
Ok(ReceivedFds {
data: data_buf,
fds,
})
}
fn recv_fds_into<const N: usize>(
&self,
data_buf: &mut [u8],
) -> io::Result<(usize, Vec<OwnedFd>)> {
recv_fds_into_impl::<N>(self.as_fd(), data_buf)
}
}