#![allow(clippy::unnecessary_wraps)]
use std::os::fd::AsRawFd;
use io_uring::{opcode, types};
use wireshift_core::op::OpDescriptor;
use wireshift_core::{Error, Result};
use super::PreparedEntry;
use crate::file_registry::FileRegistryTable;
use crate::helpers::sockaddr_to_storage;
use crate::types::InflightData;
pub(crate) fn build_connect(
descriptor: &OpDescriptor,
id: u64,
_fixed_files: &FileRegistryTable,
) -> Result<Vec<PreparedEntry>> {
let OpDescriptor::Connect { addr, .. } = descriptor else {
unreachable!()
};
let domain = match addr {
std::net::SocketAddr::V4(_) => libc::AF_INET,
std::net::SocketAddr::V6(_) => libc::AF_INET6,
};
let socket_fd = unsafe {
libc::socket(
domain,
libc::SOCK_STREAM | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC,
0,
)
};
if socket_fd < 0 {
return Err(Error::io(
"failed to create socket for io_uring connect",
std::io::Error::last_os_error(),
"ensure the system has available file descriptors",
));
}
let (storage, addr_len) = sockaddr_to_storage(addr);
let storage = Box::new(storage);
let entry = opcode::Connect::new(
types::Fd(socket_fd),
std::ptr::from_ref::<libc::sockaddr_storage>(storage.as_ref()).cast::<libc::sockaddr>(),
addr_len,
)
.build()
.user_data(id);
Ok(vec![PreparedEntry {
entry,
pinned: InflightData::Sockaddr {
socket_fd,
storage,
addr_len,
},
link_to_next: None,
}])
}
pub(crate) fn build_accept(
descriptor: &OpDescriptor,
id: u64,
_fixed_files: &FileRegistryTable,
) -> Result<Vec<PreparedEntry>> {
let OpDescriptor::Accept { listener } = descriptor else {
unreachable!()
};
let entry = opcode::Accept::new(
types::Fd(listener.as_raw_fd()),
std::ptr::null_mut(),
std::ptr::null_mut(),
)
.flags(libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC)
.build()
.user_data(id);
Ok(vec![PreparedEntry {
entry,
pinned: InflightData::None,
link_to_next: None,
}])
}
pub(crate) fn build_send(
descriptor: &OpDescriptor,
id: u64,
fixed_files: &FileRegistryTable,
) -> Result<Vec<PreparedEntry>> {
let OpDescriptor::Send { stream, buffer } = descriptor else {
unreachable!()
};
let len = u32::try_from(buffer.filled_len()).map_err(|_| {
Error::validation(
"send buffer length exceeds io_uring's u32 limit",
"use smaller buffers for native io_uring sends",
)
})?;
let entry = if let Some(slot) = fixed_files.lookup(stream.as_raw_fd()) {
opcode::Send::new(types::Fixed(slot), buffer.backend_ref().as_ptr(), len)
} else {
opcode::Send::new(
types::Fd(stream.as_raw_fd()),
buffer.backend_ref().as_ptr(),
len,
)
}
.build()
.user_data(id);
Ok(vec![PreparedEntry {
entry,
pinned: InflightData::None,
link_to_next: None,
}])
}
pub(crate) fn build_recv(
descriptor: &OpDescriptor,
id: u64,
fixed_files: &FileRegistryTable,
) -> Result<Vec<PreparedEntry>> {
let OpDescriptor::Recv { stream, buffer } = descriptor else {
unreachable!()
};
let len = u32::try_from(buffer.capacity()).map_err(|_| {
Error::validation(
"recv buffer length exceeds io_uring's u32 limit",
"use smaller buffers for native io_uring receives",
)
})?;
let entry = if let Some(slot) = fixed_files.lookup(stream.as_raw_fd()) {
opcode::Recv::new(
types::Fixed(slot),
buffer.backend_ref().as_ptr().cast_mut(),
len,
)
} else {
opcode::Recv::new(
types::Fd(stream.as_raw_fd()),
buffer.backend_ref().as_ptr().cast_mut(),
len,
)
}
.build()
.user_data(id);
Ok(vec![PreparedEntry {
entry,
pinned: InflightData::None,
link_to_next: None,
}])
}