use std::marker::PhantomData;
use crate::socket::Timestamp;
pub(crate) const fn control_message_space<T>() -> usize {
(unsafe { libc::CMSG_SPACE((std::mem::size_of::<T>()) as _) }) as usize
}
pub(crate) enum MessageQueue {
Normal,
#[cfg(target_os = "linux")]
Error,
}
pub(crate) struct ControlMessageIterator<'a> {
mhdr: libc::msghdr,
next_msg: *const libc::cmsghdr,
phantom: PhantomData<&'a [u8]>,
}
impl ControlMessageIterator<'_> {
pub unsafe fn new(mhdr: libc::msghdr) -> Self {
let current_msg = if mhdr.msg_flags & libc::MSG_CTRUNC == 0 {
unsafe { libc::CMSG_FIRSTHDR(&mhdr) }
} else {
std::ptr::null()
};
Self {
mhdr,
next_msg: current_msg,
phantom: PhantomData,
}
}
}
pub(crate) enum ControlMessage {
Timestamping {
software: Option<Timestamp>,
hardware: Option<Timestamp>,
},
#[cfg(target_os = "linux")]
ReceiveError(libc::sock_extended_err),
Other(libc::cmsghdr),
}
#[cfg(target_os = "linux")]
const SCM_TIMESTAMP_NS: libc::c_int = libc::SCM_TIMESTAMPNS;
#[cfg(target_os = "freebsd")]
const SCM_TIMESTAMP_NS: libc::c_int = libc::SCM_REALTIME;
#[cfg(target_os = "linux")]
const PACKET_TX_TIMESTAMP: libc::c_int = 16;
impl Iterator for ControlMessageIterator<'_> {
type Item = ControlMessage;
fn next(&mut self) -> Option<Self::Item> {
let current_msg = unsafe { self.next_msg.as_ref() }?;
self.next_msg = unsafe { libc::CMSG_NXTHDR(&self.mhdr, self.next_msg) };
Some(match (current_msg.cmsg_level, current_msg.cmsg_type) {
#[cfg(target_os = "linux")]
(libc::SOL_SOCKET, libc::SCM_TIMESTAMPING) => {
let cmsg_data =
unsafe { libc::CMSG_DATA(current_msg) } as *const [libc::timespec; 3];
let [software, _, hardware] = unsafe { std::ptr::read_unaligned(cmsg_data) };
let hardware = if hardware.tv_sec != 0 || hardware.tv_nsec != 0 {
Some(Timestamp::from_timespec(hardware))
} else {
None
};
let software = if software.tv_sec != 0 || software.tv_nsec != 0 {
Some(Timestamp::from_timespec(software))
} else {
None
};
ControlMessage::Timestamping { software, hardware }
}
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
(libc::SOL_SOCKET, SCM_TIMESTAMP_NS) => {
let cmsg_data = unsafe { libc::CMSG_DATA(current_msg) } as *const libc::timespec;
let timespec = unsafe { std::ptr::read_unaligned(cmsg_data) };
ControlMessage::Timestamping {
software: Some(Timestamp::from_timespec(timespec)),
hardware: None,
}
}
(libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => {
let cmsg_data = unsafe { libc::CMSG_DATA(current_msg) } as *const libc::timeval;
let timeval = unsafe { std::ptr::read_unaligned(cmsg_data) };
ControlMessage::Timestamping {
software: Some(Timestamp::from_timeval(timeval)),
hardware: None,
}
}
#[cfg(target_os = "linux")]
(libc::SOL_IP, libc::IP_RECVERR)
| (libc::SOL_IPV6, libc::IPV6_RECVERR)
| (libc::SOL_PACKET, PACKET_TX_TIMESTAMP) => {
let error = unsafe {
let ptr = libc::CMSG_DATA(current_msg) as *const libc::sock_extended_err;
std::ptr::read_unaligned(ptr)
};
ControlMessage::ReceiveError(error)
}
_ => ControlMessage::Other(*current_msg),
})
}
}
pub(crate) fn zeroed_sockaddr_storage() -> libc::sockaddr_storage {
unsafe { std::mem::MaybeUninit::zeroed().assume_init() }
}
pub(crate) fn empty_msghdr() -> libc::msghdr {
unsafe { std::mem::MaybeUninit::<libc::msghdr>::zeroed().assume_init() }
}