use std::net::SocketAddr;
#[cfg(unix)]
use std::os::fd::{AsRawFd, FromRawFd, RawFd};
use socket2::SockAddr;
#[cfg(target_os = "linux")]
use crate::buf::PooledBuf;
use crate::{
buf::{BufResult, IoBuf, IoBufMut, IoVectoredBuf, IoVectoredBufMut},
driver::helpers::socket::Socket,
};
#[cfg(windows)]
use std::os::windows::io::{AsRawSocket, FromRawSocket, RawSocket};
pub struct UdpSocket {
pub(super) inner: Socket,
}
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct RecvFlags(u32);
#[cfg(target_os = "linux")]
impl RecvFlags {
pub(crate) fn from_bits(bits: u32) -> Self {
Self(bits)
}
pub fn bits(self) -> u32 {
self.0
}
pub fn is_truncated(self) -> bool {
self.0 & libc::MSG_TRUNC as u32 != 0
}
pub fn is_control_truncated(self) -> bool {
self.0 & libc::MSG_CTRUNC as u32 != 0
}
}
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
#[derive(Debug)]
pub struct RecvDatagram {
pub buffer: PooledBuf,
pub peer: Option<SocketAddr>,
pub flags: RecvFlags,
pub original_len: usize,
}
#[cfg(target_os = "linux")]
impl RecvDatagram {
pub(crate) fn new(buffer: PooledBuf, peer: Option<SocketAddr>, flags: u32, original_len: usize) -> Self {
Self {
buffer,
peer,
flags: RecvFlags::from_bits(flags),
original_len,
}
}
pub fn is_truncated(&self) -> bool {
self.flags.is_truncated() || self.original_len > self.buffer.len()
}
}
impl UdpSocket {
pub async fn bind(socket_addr: SocketAddr) -> std::io::Result<UdpSocket> {
let socket = Socket::bind(socket_addr, socket2::Type::DGRAM)?;
socket.set_async_flags()?;
Ok(UdpSocket { inner: socket })
}
pub async fn close(self) -> std::io::Result<()> {
self.inner.close().await
}
pub fn local_addr(&self) -> std::io::Result<SocketAddr> {
self.inner
.handle
.local_addr()?
.as_socket()
.ok_or_else(|| std::io::Error::other("Could not get socket IP address"))
}
pub async fn connect(&self, socket_addr: SocketAddr) -> std::io::Result<()> {
self.inner.connect(SockAddr::from(socket_addr)).await
}
pub async fn send<B: IoBuf>(&self, buf: B) -> BufResult<usize, B> {
self.inner.send(buf).await
}
pub async fn send_to<B: IoBuf>(&self, buf: B, socket_addr: SocketAddr) -> BufResult<usize, B> {
self.inner.send_to(buf, socket_addr).await
}
pub async fn sendmsg<V: IoVectoredBuf, C: IoBuf>(
&self,
io_slices: V,
socket_addr: Option<SocketAddr>,
msg_control: Option<C>,
) -> BufResult<(usize, Option<C>), V> {
self.inner.sendmsg(io_slices, socket_addr, msg_control).await
}
pub async fn recv<B: IoBufMut>(&self, buf: B) -> BufResult<usize, B> {
self.inner.recv(buf).await
}
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
pub async fn recv_managed(&self, len: usize) -> std::io::Result<RecvDatagram> {
self.inner.recv_datagram_managed(len, false).await
}
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
pub fn recv_multi(&self) -> std::io::Result<impl crate::io::Stream<Item = std::io::Result<RecvDatagram>> + use<>> {
self.inner.recv_datagram_multi(false)
}
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
pub async fn recv_from_managed(&self, len: usize) -> std::io::Result<RecvDatagram> {
self.inner.recv_from_managed(len).await
}
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(target_os = "linux")))]
pub fn recv_from_multi(
&self,
) -> std::io::Result<impl crate::io::Stream<Item = std::io::Result<RecvDatagram>> + use<>> {
self.inner.recv_from_multi()
}
pub async fn recv_from<B: IoBufMut>(&self, buf: B) -> BufResult<(usize, SocketAddr), B> {
self.inner.recv_from(buf).await
}
pub async fn recvmsg<V: IoVectoredBufMut>(&self, buf: V) -> BufResult<(usize, SocketAddr), V> {
self.inner.recvmsg(buf).await
}
pub fn shutdown(&self, how: std::net::Shutdown) -> std::io::Result<()> {
self.inner.shutdown(how)
}
}
#[cfg(unix)]
impl FromRawFd for UdpSocket {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
UdpSocket::from(unsafe { Socket::from_raw_fd(fd) })
}
}
#[cfg(unix)]
impl AsRawFd for UdpSocket {
fn as_raw_fd(&self) -> RawFd {
self.inner.as_raw_fd()
}
}
#[cfg(windows)]
impl FromRawSocket for UdpSocket {
unsafe fn from_raw_socket(socket: RawSocket) -> Self {
UdpSocket::from(unsafe { Socket::from_raw_socket(socket) })
}
}
#[cfg(windows)]
impl AsRawSocket for UdpSocket {
fn as_raw_socket(&self) -> RawSocket {
self.inner.as_raw_socket()
}
}
impl From<Socket> for UdpSocket {
fn from(inner: Socket) -> Self {
Self { inner }
}
}
impl UdpSocket {
pub fn from_std(socket: std::net::UdpSocket) -> std::io::Result<Self> {
let inner = Socket::from_socket(socket2::Socket::from(socket))?;
Ok(Self { inner })
}
}