#![cfg(any(unix, windows))]
#![allow(dead_code)]
use core::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::time::SystemTime;
#[cfg(test)]
mod tests;
#[cfg(unix)]
pub(crate) use unix::*;
#[cfg(unix)]
mod unix;
#[derive(Debug, Clone, Copy)]
pub struct RecvMeta {
peer: SocketAddr,
local_ip: IpAddr,
interface_index: u32,
hop_limit: Option<u8>,
kernel_rx_time: Option<SystemTime>,
len: usize,
truncated: bool,
}
impl RecvMeta {
pub(crate) fn empty(peer: SocketAddr) -> Self {
let local_ip = match peer {
SocketAddr::V4(_) => IpAddr::V4(Ipv4Addr::UNSPECIFIED),
SocketAddr::V6(_) => IpAddr::V6(Ipv6Addr::UNSPECIFIED),
};
Self {
peer,
local_ip,
interface_index: 0,
hop_limit: None,
kernel_rx_time: None,
len: 0,
truncated: false,
}
}
#[inline(always)]
pub(crate) const fn peer(&self) -> SocketAddr {
self.peer
}
#[inline(always)]
pub(crate) const fn local_ip(&self) -> IpAddr {
self.local_ip
}
#[inline(always)]
pub(crate) const fn interface_index(&self) -> u32 {
self.interface_index
}
#[inline(always)]
pub(crate) const fn hop_limit(&self) -> Option<u8> {
self.hop_limit
}
#[inline(always)]
pub(crate) const fn kernel_rx_time(&self) -> Option<SystemTime> {
self.kernel_rx_time
}
#[inline(always)]
pub(crate) const fn truncated(&self) -> bool {
self.truncated
}
#[cfg(test)]
pub(crate) const fn new(
peer: SocketAddr,
local_ip: IpAddr,
interface_index: u32,
hop_limit: Option<u8>,
kernel_rx_time: Option<SystemTime>,
len: usize,
) -> Self {
Self {
peer,
local_ip,
interface_index,
hop_limit,
kernel_rx_time,
len,
truncated: false,
}
}
#[cfg(test)]
pub(crate) const fn with_truncated(mut self) -> Self {
self.truncated = true;
self
}
}
pub struct Socket {
inner: compio_net::UdpSocket,
}
impl Socket {
pub async fn from_std(sock: std::net::UdpSocket) -> std::io::Result<Self> {
sock.set_nonblocking(true)?;
#[cfg(unix)]
{
enable_recv_cmsgs(&sock)?;
}
let inner = compio_net::UdpSocket::from_std(sock)?;
Ok(Self { inner })
}
pub async fn recv(&self, max: usize) -> std::io::Result<(Vec<u8>, RecvMeta)> {
let buf: Vec<u8> = Vec::with_capacity(max + 1);
#[cfg(unix)]
{
let ctrl = AlignedCtrlBuf::new();
let compio_buf::BufResult(res, (buf, ctrl)) = self.inner.recv_msg(buf, ctrl).await;
let (data_len, ctrl_len, peer, _recv_flags) = res?;
let mut data = buf;
if data.len() > data_len {
data.truncate(data_len);
}
let mut meta = RecvMeta::empty(peer);
meta.len = data_len;
meta.truncated = data_len > max;
let ctrl_bytes = ctrl.filled(ctrl_len);
decode_unix_cmsgs(ctrl_bytes, &mut meta);
Ok((data, meta))
}
#[cfg(not(unix))]
{
let compio_buf::BufResult(res, buf) = self.inner.recv_from(buf).await;
let (data_len, peer) = res?;
let mut data = buf;
if data.len() > data_len {
data.truncate(data_len);
}
let mut meta = RecvMeta::empty(peer);
meta.len = data_len;
meta.truncated = data_len > max;
Ok((data, meta))
}
}
pub async fn send_to(
&self,
buf: &[u8],
dst: core::net::SocketAddr,
ctrl: Option<&[u8]>,
) -> std::io::Result<usize> {
let data = buf.to_vec();
match ctrl {
#[cfg(unix)]
Some(c) if !c.is_empty() => {
let ctrl_buf = AlignedCtrlBuf::from_slice(c);
let compio_buf::BufResult(res, _) = self.inner.send_msg(data, ctrl_buf, dst).await;
res
}
#[cfg(not(unix))]
Some(_) => {
let compio_buf::BufResult(res, _) = self.inner.send_to(data, dst).await;
res
}
_ => {
let compio_buf::BufResult(res, _) = self.inner.send_to(data, dst).await;
res
}
}
}
}