#![cfg(all(feature = "net", target_os = "linux"))]
use std::io;
use std::mem;
use std::ptr;
use socket2::{SockAddr, SockAddrStorage};
use crate::buf::{BufferPool, SetLen};
use crate::driver::backends::iouring::{MultishotCleanup, Submission as UringSubmission, UringMultishotOperation};
use crate::driver::helpers::io_handle::SharedIoHandle;
use crate::driver::ops::{Completion, MultiOp};
use crate::net::udp::RecvDatagram;
use crate::runtime::local::CURRENT_DRIVER;
pub(crate) struct RecvFromMulti {
io_handle: SharedIoHandle<socket2::Socket>,
pool: BufferPool,
buffer_group: u16,
msghdr: Box<libc::msghdr>,
}
impl MultiOp<RecvFromMulti> {
pub(crate) fn recv_datagram_multi(
io_handle: &SharedIoHandle<socket2::Socket>,
capture_peer: bool,
) -> io::Result<Self> {
let pool = CURRENT_DRIVER.with(|h| h.upgrade().expect("Not in a runtime context").buffer_pool())?;
let buffer_group = pool.buffer_group()?;
let mut msghdr: Box<libc::msghdr> = Box::new(unsafe { mem::zeroed() });
if capture_peer {
msghdr.msg_namelen = mem::size_of::<libc::sockaddr_storage>() as _;
}
msghdr.msg_controllen = 0;
let data = RecvFromMulti {
io_handle: io_handle.clone(),
pool,
buffer_group,
msghdr,
};
CURRENT_DRIVER.with(|handle| Ok(handle.upgrade().expect("Not in a runtime context").defer_multi_op(data)))
}
}
fn parse_item(pool: &BufferPool, msghdr: &libc::msghdr, completion: &Completion) -> io::Result<RecvDatagram> {
let bid = io_uring::cqueue::buffer_select(completion.flags)
.ok_or_else(|| io::Error::other("multishot recv_from completed without a buffer id"))?;
let mut buf = pool
.take(bid)?
.ok_or_else(|| io::Error::other("multishot recv_from buffer id missing from pool"))?;
let n = *completion.result.as_ref().map_err(|e| {
io::Error::new(e.kind(), e.to_string())
})?;
unsafe { buf.set_len(n as usize) };
let parsed = io_uring::types::RecvMsgOut::parse(&buf, msghdr).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidData,
"failed to parse multishot recvmsg buffer layout",
)
})?;
let peer = if msghdr.msg_namelen > 0 {
let name = parsed.name_data();
if name.is_empty() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"multishot recv_from returned empty peer address",
));
}
let mut storage = SockAddrStorage::zeroed();
let copy_len = name.len().min(mem::size_of::<libc::sockaddr_storage>());
Some(unsafe {
ptr::copy_nonoverlapping(name.as_ptr(), storage.view_as::<u8>(), copy_len);
let sock = SockAddr::new(storage, copy_len as _);
sock.as_socket().ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
"invalid peer address in multishot recv_from",
)
})?
})
} else {
None
};
let payload = parsed.payload_data();
let payload_offset = unsafe { payload.as_ptr().offset_from(buf.as_ptr()) as usize };
let payload_len = payload.len();
let original_len = parsed.incoming_payload_len() as usize;
let flags = parsed.flags();
buf.set_view(payload_offset..payload_offset + payload_len)?;
Ok(RecvDatagram::new(buf, peer, flags, original_len))
}
unsafe impl UringMultishotOperation for RecvFromMulti {
type Item = io::Result<RecvDatagram>;
fn submit(&mut self) -> UringSubmission {
use io_uring::{opcode, types};
opcode::RecvMsgMulti::new(
types::Fd(self.io_handle.raw_fd()),
self.msghdr.as_ref() as *const _,
self.buffer_group,
)
.build()
}
fn complete_item(&mut self, completion: Completion) -> Option<Self::Item> {
Some(match completion.result {
Ok(_) => parse_item(&self.pool, &self.msghdr, &completion),
Err(err) => {
if let Some(bid) = io_uring::cqueue::buffer_select(completion.flags) {
self.pool.recycle_selected(bid);
}
Err(err)
}
})
}
fn submission_error(error: io::Error) -> Self::Item {
Err(error)
}
fn completion_cleanup(&self) -> MultishotCleanup {
MultishotCleanup::ProvidedBuffer(self.pool.clone())
}
}