use crate::platform::Fd;
use crate::string::unix_str::UnixStr;
use crate::Error;
#[derive(Debug, Copy, Clone)]
pub struct AddressFamily(pub(crate) u16);
impl AddressFamily {
pub const AF_UNSPEC: Self = Self(0);
pub const AF_UNIX: Self = Self(1);
pub const AF_LOCAL: Self = Self(1);
pub const AF_INET: Self = Self(2);
pub const AF_AX25: Self = Self(3);
pub const AF_IPX: Self = Self(4);
pub const AF_APPLETALK: Self = Self(5);
pub const AF_NETROM: Self = Self(6);
pub const AF_BRIDGE: Self = Self(7);
pub const AF_ATMPVC: Self = Self(8);
pub const AF_X25: Self = Self(9);
pub const AF_INET6: Self = Self(10);
pub const AF_ROSE: Self = Self(11);
#[expect(non_upper_case_globals)]
pub const AF_DECnet: Self = Self(12);
pub const AF_NETBEUI: Self = Self(13);
pub const AF_SECURITY: Self = Self(14);
pub const AF_KEY: Self = Self(15);
pub const AF_NETLINK: Self = Self(16);
pub const AF_ROUTE: Self = Self(Self::AF_NETLINK.0);
pub const AF_PACKET: Self = Self(17);
pub const AF_ASH: Self = Self(18);
pub const AF_ECONET: Self = Self(19);
pub const AF_ATMSVC: Self = Self(20);
pub const AF_RDS: Self = Self(21);
pub const AF_SNA: Self = Self(22);
pub const AF_IRDA: Self = Self(23);
pub const AF_PPPOX: Self = Self(24);
pub const AF_WANPIPE: Self = Self(25);
pub const AF_LLC: Self = Self(26);
pub const AF_IB: Self = Self(27);
pub const AF_MPLS: Self = Self(28);
pub const AF_CAN: Self = Self(29);
pub const AF_TIPC: Self = Self(30);
pub const AF_BLUETOOTH: Self = Self(31);
pub const AF_IUCV: Self = Self(32);
pub const AF_RXRPC: Self = Self(33);
pub const AF_ISDN: Self = Self(34);
pub const AF_PHONET: Self = Self(35);
pub const AF_IEEE802154: Self = Self(36);
pub const AF_CAIF: Self = Self(37);
pub const AF_ALG: Self = Self(38);
pub const AF_NFC: Self = Self(39);
pub const AF_VSOCK: Self = Self(40);
pub const AF_KCM: Self = Self(41);
pub const AF_QIPCRTR: Self = Self(42);
pub const AF_SMC: Self = Self(43);
pub const AF_XDP: Self = Self(44);
pub const AF_MCTP: Self = Self(45);
pub const AF_MAX: Self = Self(46);
}
#[derive(Debug, Copy, Clone)]
pub struct SocketOptions(pub(crate) u32);
impl SocketOptions {
#[inline]
#[must_use]
pub const fn new(socket_type: SocketType, socket_flags: SocketFlags) -> Self {
Self(socket_type.0 | socket_flags.0)
}
}
#[repr(transparent)]
#[derive(Debug, Copy, Clone)]
pub struct SocketType(pub(crate) u32);
impl SocketType {
pub const SOCK_STREAM: Self = Self(1);
pub const SOCK_DGRAM: Self = Self(2);
pub const SOCK_RAW: Self = Self(3);
pub const SOCK_RDM: Self = Self(4);
pub const SOCK_SEQPACKET: Self = Self(5);
pub const SOCK_PACKET: Self = Self(10);
}
transparent_bitflags!(
pub struct SocketFlags: u32 {
const DEFAULT = 0;
const SOCK_NONBLOCK = linux_rust_bindings::fcntl::O_NONBLOCK as u32;
const SOCK_CLOEXEC = linux_rust_bindings::fcntl::O_CLOEXEC as u32;
}
);
#[repr(transparent)]
#[derive(Debug, Copy, Clone)]
pub struct SocketAddressInet(pub(crate) linux_rust_bindings::socket::sockaddr_in);
impl SocketAddressInet {
pub const LENGTH: usize = core::mem::size_of::<linux_rust_bindings::socket::sockaddr_in>();
#[must_use]
pub const fn new(ip_addr: [u8; 4], port: u16) -> Self {
let s_addr = u32::from_le_bytes(ip_addr);
let sin_port = port.to_be();
let i = linux_rust_bindings::socket::sockaddr_in {
sin_family: AddressFamily::AF_INET.0,
sin_port,
sin_addr: linux_rust_bindings::socket::in_addr { s_addr },
__pad: [0u8; 8],
};
Self(i)
}
#[must_use]
pub const fn ipv4_addr(&self) -> ([u8; 4], u16) {
#[cfg(target_endian = "big")]
{
(self.0.sin_addr.s_addr.to_le_bytes(), self.0.sin_port)
}
#[cfg(target_endian = "little")]
{
let bytes = self.0.sin_port.to_be_bytes();
(
self.0.sin_addr.s_addr.to_le_bytes(),
u16::from_le_bytes(bytes),
)
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct SocketArgUnix {
pub(crate) addr: SocketAddressUnix,
pub(crate) addr_len: usize,
}
#[repr(transparent)]
#[derive(Debug, Copy, Clone)]
pub struct SocketAddressUnix(pub(crate) linux_rust_bindings::socket::sockaddr_un);
impl SocketAddressUnix {
#[inline]
#[must_use]
pub const fn family(&self) -> AddressFamily {
AddressFamily(self.0.sun_family)
}
#[inline]
#[must_use]
pub const fn path_raw(&self) -> [core::ffi::c_char; 108] {
self.0.sun_path
}
pub fn try_from_unix(path: &UnixStr) -> crate::Result<SocketArgUnix> {
let mut ind = 0;
let buf = unsafe {
let mut buf = [0; 108];
let mut ptr = path.as_ptr();
while !ptr.is_null() {
let val = core::ffi::c_char::try_from(ptr.read()).map_err(|_e| {
Error::no_code(
"Socket paths need to be 7-bit ASCII, path contained value in 8-bit range",
)
})?;
buf[ind] = val;
if ind == 107 && buf[ind] != 0 {
return Err(Error::no_code("Socket address too long"));
} else if buf[ind] == 0 {
ind += 1;
break;
}
ptr = ptr.add(1);
ind += 1;
}
buf
};
let addr = Self(linux_rust_bindings::socket::sockaddr_un {
sun_family: AddressFamily::AF_UNIX.0,
sun_path: buf,
});
Ok(SocketArgUnix {
addr,
addr_len: ind
+ core::mem::size_of::<linux_rust_bindings::socket::__kernel_sa_family_t>(),
})
}
}
#[cfg(feature = "alloc")]
macro_rules! cmsg_len {
($len: expr) => {
cmsg_align!(core::mem::size_of::<CmsgHdr>()) + $len
};
}
#[cfg(feature = "alloc")]
macro_rules! cmsg_space {
($len: expr) => {
cmsg_align!($len) + cmsg_align!(core::mem::size_of::<CmsgHdr>())
};
}
#[cfg(feature = "alloc")]
macro_rules! cmsg_align {
($len: expr) => {
($len + core::mem::size_of::<usize>() - 1) & !(core::mem::size_of::<usize>() - 1)
};
}
#[cfg(feature = "alloc")]
macro_rules! cmsg_firsthdr {
($mhdr: expr) => {
if $mhdr.msg_controllen >= core::mem::size_of::<CmsgHdr>() {
$mhdr.msg_control
} else {
core::ptr::null_mut()
}
};
}
#[cfg(feature = "alloc")]
macro_rules! cmsg_nxthdr {
($mhdr: expr, $cmsg: expr) => {
if ((*$cmsg).cmsg_len) < core::mem::size_of::<CmsgHdr>()
|| __cmsg_len!($cmsg) + core::mem::size_of::<CmsgHdr>()
>= __mhdr_end!($mhdr) - core::ptr::addr_of!($cmsg) as usize
{
core::ptr::null()
} else {
__cmsg_next!($cmsg) as *const CmsgHdr
}
};
}
#[cfg(feature = "alloc")]
macro_rules! cmsg_data {
($cmsg: expr) => {
($cmsg.cast::<CmsgHdr>()).add(1).cast::<u8>()
};
}
#[cfg(feature = "alloc")]
macro_rules! __cmsg_len {
($cmsg: expr) => {
((*$cmsg).cmsg_len + 8usize - 1usize) & !((8u64 - 1u64) as usize)
};
}
#[cfg(feature = "alloc")]
macro_rules! __cmsg_next {
($cmsg: expr) => {
$cmsg as usize + __cmsg_len!($cmsg)
};
}
#[cfg(feature = "alloc")]
macro_rules! __mhdr_end {
($mhdr: expr) => {
core::ptr::addr_of!($mhdr.msg_control) as usize + $mhdr.msg_controllen
};
}
#[derive(Debug)]
#[cfg(feature = "alloc")]
pub enum ControlMessageSend<'a> {
ScmRights(&'a [crate::platform::Fd]),
}
#[repr(C)]
#[derive(Debug)]
#[cfg(feature = "alloc")]
pub struct MsgHdrBorrow<'a> {
msg_name: *const u8,
msg_namelen: u32,
msg_iov: *mut linux_rust_bindings::uio::iovec,
msg_iovlen: usize,
msg_control: *mut CmsgHdrSend<'a>,
msg_controllen: usize,
msg_flags: i32,
}
#[cfg(feature = "alloc")]
pub struct SendDropGuard<'a> {
pub(crate) msghdr: MsgHdrBorrow<'a>,
_dealloc_spc: alloc::vec::Vec<u8>,
}
#[cfg(feature = "alloc")]
#[expect(clippy::cast_possible_truncation)]
impl<'a> MsgHdrBorrow<'a> {
#[must_use]
pub fn create_send(
name: Option<&'a UnixStr>,
io: &'a [crate::platform::IoSlice<'a>],
control: Option<ControlMessageSend<'a>>,
) -> SendDropGuard<'a> {
let (name, name_len) = if let Some(name) = name {
(name.as_ptr(), name.len() as u32)
} else {
(core::ptr::null(), 0)
};
unsafe {
if let Some(ctrl) = control {
match ctrl {
ControlMessageSend::ScmRights(fds) => {
let spc = cmsg_space!(core::mem::size_of_val(fds));
let mut cmsg_raw = alloc::vec![0u8; spc];
let cmsg_ptr = cmsg_raw.as_mut_ptr();
let mhdr = MsgHdrBorrow {
msg_name: name,
msg_namelen: name_len,
msg_iov: io.as_ptr().cast_mut().cast(),
msg_iovlen: io.len(),
msg_control: cmsg_ptr.cast(),
msg_controllen: spc,
msg_flags: 0,
};
let cmhdr: *mut CmsgHdr = cmsg_firsthdr!(mhdr).cast::<CmsgHdr>();
let mut_cm = cmhdr.as_mut().unwrap_unchecked();
let cmsg_len = cmsg_len!(core::mem::size_of_val(fds));
mut_cm.cmsg_level = 1;
mut_cm.cmsg_type = 0x01;
mut_cm.cmsg_len = cmsg_len;
let data = cmsg_data!(cmhdr);
core::ptr::copy_nonoverlapping(
fds.as_ptr().cast::<u8>(),
data,
core::mem::size_of_val(fds),
);
SendDropGuard {
msghdr: mhdr,
_dealloc_spc: cmsg_raw,
}
}
}
} else {
SendDropGuard {
msghdr: MsgHdrBorrow {
msg_name: name,
msg_namelen: name_len,
msg_iov: io.as_ptr().cast_mut().cast(),
msg_iovlen: io.len(),
msg_control: core::ptr::null_mut(),
msg_controllen: 0,
msg_flags: 0,
},
_dealloc_spc: alloc::vec::Vec::new(),
}
}
}
}
#[must_use]
pub fn create_recv(
io: &'a mut [crate::platform::IoSliceMut<'a>],
cmsg_buf: Option<&'a mut [u8]>,
) -> Self {
let (ctrl, ctrl_len) = if let Some(cm_buf) = cmsg_buf {
(cm_buf.as_mut_ptr(), cm_buf.len())
} else {
(core::ptr::null_mut(), 0)
};
MsgHdrBorrow {
msg_name: core::ptr::null(),
msg_namelen: 0,
msg_iov: io.as_mut_ptr().cast(),
msg_iovlen: io.len(),
msg_control: ctrl.cast(),
msg_controllen: ctrl_len,
msg_flags: 0,
}
}
#[must_use]
pub fn control_messages(&'a self) -> ControlMessageIterator<'a> {
let first = cmsg_firsthdr!(self);
let cmsg_prev = if first.is_null() {
None
} else {
Some(first.cast())
};
ControlMessageIterator {
msghdr: self,
cmsg_prev,
}
}
}
#[cfg(feature = "alloc")]
pub struct ControlMessageIterator<'a> {
msghdr: &'a MsgHdrBorrow<'a>,
cmsg_prev: Option<*mut CmsgHdr>,
}
#[cfg(feature = "alloc")]
impl<'a> Iterator for ControlMessageIterator<'a> {
type Item = ControlMessageSend<'a>;
fn next(&mut self) -> Option<Self::Item> {
let cmsg = self.cmsg_prev?;
unsafe {
let r = cmsg.as_mut()?;
if r.cmsg_type == 1 && r.cmsg_level == 1 {
let data = cmsg_data!(cmsg);
let len = cmsg.cast_const() as usize + r.cmsg_len - data as usize;
let len = len / core::mem::size_of::<crate::platform::Fd>();
self.cmsg_prev = Some(cmsg_nxthdr!(self.msghdr, cmsg).cast_mut());
#[expect(clippy::cast_ptr_alignment)]
return Some(ControlMessageSend::ScmRights(core::slice::from_raw_parts(
data as *const crate::platform::NonNegativeI32,
len,
)));
}
self.cmsg_prev = Some(cmsg_nxthdr!(self.msghdr, cmsg).cast_mut());
self.next()
}
}
}
#[repr(C)]
#[derive(Debug)]
#[cfg(feature = "alloc")]
pub struct CmsgHdrSend<'a> {
cmsg_len: usize,
cmsg_level: i32,
cmsg_type: i32,
_pd: core::marker::PhantomData<&'a ()>,
}
#[repr(C)]
#[cfg(feature = "alloc")]
pub struct MsgHdr {
pub msg_name: *const u8,
pub msg_namelen: u32,
pub msg_iov: *mut linux_rust_bindings::uio::iovec,
pub msg_iovlen: usize,
pub msg_control: *mut CmsgHdr,
pub msg_controllen: usize,
pub msg_flags: i32,
}
#[derive(Debug)]
pub enum ControlMessageRaw {
ScmRights(*mut Fd, usize),
}
#[cfg(feature = "alloc")]
impl MsgHdr {
#[cfg(feature = "alloc")]
pub unsafe fn update_control(
&mut self,
control: Option<ControlMessageSend>,
cmsg_ptr: *mut u8,
) {
unsafe {
if let Some(ctrl) = control {
match ctrl {
ControlMessageSend::ScmRights(fds) => {
let spc = cmsg_space!(core::mem::size_of_val(fds));
core::ptr::write_bytes(cmsg_ptr, 0, spc);
self.msg_control = cmsg_ptr.cast();
self.msg_controllen = spc;
let cmhdr: *mut CmsgHdr = cmsg_firsthdr!(self).cast::<CmsgHdr>();
let mut_cm = cmhdr.as_mut().unwrap_unchecked();
let cmsg_len = cmsg_len!(core::mem::size_of_val(fds));
mut_cm.cmsg_level = 1;
mut_cm.cmsg_type = 0x01;
mut_cm.cmsg_len = cmsg_len;
let data = cmsg_data!(cmhdr);
core::ptr::copy_nonoverlapping(
fds.as_ptr().cast::<u8>(),
data,
core::mem::size_of_val(fds),
);
}
}
} else {
self.msg_control = core::ptr::null_mut();
self.msg_controllen = 0;
}
}
}
#[must_use]
#[cfg(feature = "alloc")]
pub unsafe fn create_send(
msg_iov: *mut linux_rust_bindings::uio::iovec,
msg_iovlen: usize,
control: Option<ControlMessageRaw>,
cmsg_ptr: *mut u8,
) -> Self {
unsafe {
if let Some(ctrl) = control {
match ctrl {
ControlMessageRaw::ScmRights(fds, len) => {
let fd_buf = core::slice::from_raw_parts_mut(fds, len);
let spc = cmsg_space!(core::mem::size_of_val(fd_buf));
core::ptr::write_bytes(cmsg_ptr, 0, spc);
let mhdr = Self {
msg_name: core::ptr::null(),
msg_namelen: 0,
msg_iov,
msg_iovlen: msg_iovlen as _,
msg_control: cmsg_ptr.cast(),
msg_controllen: spc,
msg_flags: 0,
};
let cmhdr: *mut CmsgHdr = cmsg_firsthdr!(mhdr).cast::<CmsgHdr>();
let mut_cm = cmhdr.as_mut().unwrap_unchecked();
let cmsg_len = cmsg_len!(core::mem::size_of_val(fd_buf));
mut_cm.cmsg_level = 1;
mut_cm.cmsg_type = 0x01;
mut_cm.cmsg_len = cmsg_len;
let data = cmsg_data!(cmhdr);
core::ptr::copy_nonoverlapping(
fds.cast_const().cast::<u8>(),
data,
core::mem::size_of_val(fd_buf),
);
mhdr
}
}
} else {
Self {
msg_name: core::ptr::null(),
msg_namelen: 0,
msg_iov,
msg_iovlen: msg_iovlen as _,
msg_control: core::ptr::null_mut(),
msg_controllen: 0,
msg_flags: 0,
}
}
}
}
}
#[repr(C)]
#[derive(Debug)]
#[cfg(feature = "alloc")]
pub struct CmsgHdr {
cmsg_len: usize,
cmsg_level: i32,
cmsg_type: i32,
}
#[cfg(test)]
#[cfg(feature = "alloc")]
#[expect(
clippy::cast_ptr_alignment,
clippy::ptr_cast_constness,
clippy::ptr_as_ptr,
clippy::cast_possible_truncation
)]
mod tests {
use crate::platform::{CmsgHdr, Fd, IoSlice, MsgHdr, OpenFlags};
use crate::string::unix_str::UnixStr;
use crate::unistd::open;
use alloc::vec;
use core::ptr::null_mut;
use linux_rust_bindings::uio::iovec;
#[test]
fn cmsg_macros_on_empty() {
unsafe {
let mut cmsg = CmsgHdr {
cmsg_len: 16,
cmsg_level: 0,
cmsg_type: 0,
};
let cmsg_ptr = core::ptr::addr_of_mut!(cmsg);
let len = __cmsg_len!(cmsg_ptr);
assert_eq!(16, len);
let _ = __cmsg_next!(cmsg_ptr);
let mhdr = MsgHdr {
msg_name: "abc".as_ptr(),
msg_namelen: 0,
msg_iov: null_mut(),
msg_iovlen: 0,
msg_control: cmsg_ptr,
msg_controllen: 0,
msg_flags: 0,
};
let _ = __mhdr_end!(mhdr);
assert!(cmsg_firsthdr!(mhdr).is_null());
}
}
unsafe fn msg_hdr(name: &UnixStr, iovec: &[iovec], fds: &[Fd]) -> MsgHdr {
let spc = cmsg_space!(core::mem::size_of_val(fds));
let mut cap = vec![0u8; spc];
let cmsg_ptr = cap.as_mut_ptr();
let mhdr = MsgHdr {
msg_name: name.as_ptr(),
msg_namelen: name.len() as _,
msg_iov: iovec.as_ptr().cast_mut(),
msg_iovlen: iovec.len(),
msg_control: cmsg_ptr.cast(),
msg_controllen: spc,
msg_flags: 0,
};
let cmhdr: *mut CmsgHdr = cmsg_firsthdr!(mhdr);
assert!(!cmhdr.is_null());
let mut_cm = cmhdr.as_mut().unwrap();
let cmsg_len = cmsg_len!(core::mem::size_of_val(fds));
mut_cm.cmsg_level = 1;
mut_cm.cmsg_type = 0x01;
mut_cm.cmsg_len = cmsg_len;
let data = cmsg_data!(cmhdr);
core::ptr::copy_nonoverlapping(
core::ptr::from_ref(fds) as *const u8,
data,
core::mem::size_of_val(fds),
);
core::mem::forget(cap);
mhdr
}
#[test]
fn cmsg_macros_has_single_fd() {
let name = unix_lit!("name");
let io = IoSlice::new(b"hello");
let fd1 = open(unix_lit!("/proc/mounts"), OpenFlags::O_RDONLY).unwrap();
let v = &[io.vec];
let f = &[fd1];
let mhdr = unsafe { msg_hdr(name, v, f) };
let first_hdr = cmsg_firsthdr!(mhdr);
assert!(!first_hdr.is_null());
let first_hdr_deref = unsafe { first_hdr.as_ref().unwrap() };
assert_eq!(1, first_hdr_deref.cmsg_level);
assert_eq!(1, first_hdr_deref.cmsg_type);
assert_eq!(20, first_hdr_deref.cmsg_len);
unsafe {
let data = cmsg_data!(first_hdr);
let _slice = core::slice::from_raw_parts(data as _, 8);
let len = first_hdr as *const _ as usize + first_hdr_deref.cmsg_len - data as usize;
let _gross_count = len / core::mem::size_of::<Fd>();
let null_term_ptr = data as *mut Fd;
let first = null_term_ptr.read_unaligned();
assert_eq!(fd1, first);
assert_eq!(0, cmsg_nxthdr!(mhdr, first_hdr) as usize);
}
}
#[test]
fn cmsg_macros_has_two_fd() {
let name = unix_lit!("name");
let io = IoSlice::new(b"hello");
let fd1 = open(unix_lit!("/proc/mounts"), OpenFlags::O_RDONLY).unwrap();
let v = &[io.vec];
let f = &[fd1];
let mhdr = unsafe { msg_hdr(name, v, f) };
let first_hdr = cmsg_firsthdr!(mhdr);
assert!(!first_hdr.is_null());
let first_hdr_deref = unsafe { first_hdr.as_ref().unwrap() };
assert_eq!(1, first_hdr_deref.cmsg_level);
assert_eq!(1, first_hdr_deref.cmsg_type);
assert_eq!(20, first_hdr_deref.cmsg_len);
unsafe {
let data = cmsg_data!(first_hdr);
let _slice = core::slice::from_raw_parts(data as _, 8);
let len = first_hdr as *const _ as usize + first_hdr_deref.cmsg_len - data as usize;
let _gross_count = len / core::mem::size_of::<Fd>();
let null_term_ptr = data as *mut Fd;
let first = null_term_ptr.read_unaligned();
assert_eq!(fd1, first);
assert_eq!(0, cmsg_nxthdr!(mhdr, first_hdr) as usize);
}
}
}