#[cfg(windows)]
mod platform {
use super::*;
fn set_windows_exclusive_addr_use(sock: &Socket) -> Result<(), TransportError> {
use std::os::windows::io::AsRawSocket;
use windows_sys::Win32::Networking::WinSock::{
SO_EXCLUSIVEADDRUSE, SOL_SOCKET, SOCKET, setsockopt,
};
let enabled: i32 = 1;
let result = unsafe {
setsockopt(
sock.as_raw_socket() as SOCKET,
SOL_SOCKET,
SO_EXCLUSIVEADDRUSE,
(&enabled as *const i32).cast(),
std::mem::size_of::<i32>() as i32,
)
};
if result == 0 {
Ok(())
} else {
Err(TransportError::StartFailed(format!(
"set SO_EXCLUSIVEADDRUSE failed: {}",
std::io::Error::last_os_error()
)))
}
}
const UDP_CONTROL_SEND_TIMEOUT: std::time::Duration =
std::time::Duration::from_millis(100);
pub struct UdpRawSocket {
inner: Socket,
local_addr: SocketAddr,
}
impl UdpRawSocket {
pub fn open(
bind_addr: SocketAddr,
recv_buf_size: usize,
send_buf_size: usize,
) -> Result<Self, TransportError> {
Self::open_inner(bind_addr, recv_buf_size, send_buf_size, false)
}
pub fn open_exclusive(
bind_addr: SocketAddr,
recv_buf_size: usize,
send_buf_size: usize,
) -> Result<Self, TransportError> {
Self::open_inner(bind_addr, recv_buf_size, send_buf_size, true)
}
fn open_inner(
bind_addr: SocketAddr,
recv_buf_size: usize,
send_buf_size: usize,
exclusive: bool,
) -> Result<Self, TransportError> {
let domain = if bind_addr.is_ipv4() {
Domain::IPV4
} else {
Domain::IPV6
};
let sock = Socket::new(domain, Type::DGRAM, Some(Protocol::UDP))
.map_err(|e| TransportError::StartFailed(format!("socket create failed: {}", e)))?;
sock.set_nonblocking(true).map_err(|e| {
TransportError::StartFailed(format!("set nonblocking failed: {}", e))
})?;
if exclusive {
set_windows_exclusive_addr_use(&sock)?;
} else {
let _ = sock.set_reuse_address(true);
}
sock.bind(&bind_addr.into()).map_err(|error| {
if exclusive {
TransportError::exclusive_bind_failed(bind_addr, error)
} else {
TransportError::bind_failed(bind_addr, error)
}
})?;
sock.set_recv_buffer_size(recv_buf_size)
.map_err(|e| TransportError::StartFailed(format!("set recv buffer: {}", e)))?;
sock.set_send_buffer_size(send_buf_size)
.map_err(|e| TransportError::StartFailed(format!("set send buffer: {}", e)))?;
let local_addr = sock
.local_addr()
.map_err(|e| TransportError::StartFailed(format!("get local addr: {}", e)))?
.as_socket()
.ok_or_else(|| {
TransportError::StartFailed("local address is not an IP socket".into())
})?;
Ok(Self {
inner: sock,
local_addr,
})
}
pub fn adopt(
socket: std::net::UdpSocket,
recv_buf_size: usize,
send_buf_size: usize,
) -> Result<Self, TransportError> {
let sock = Socket::from(socket);
sock.set_nonblocking(true).map_err(|e| {
TransportError::StartFailed(format!("set nonblocking failed: {}", e))
})?;
sock.set_recv_buffer_size(recv_buf_size)
.map_err(|e| TransportError::StartFailed(format!("set recv buffer: {}", e)))?;
sock.set_send_buffer_size(send_buf_size)
.map_err(|e| TransportError::StartFailed(format!("set send buffer: {}", e)))?;
let local_addr = sock
.local_addr()
.map_err(|e| TransportError::StartFailed(format!("get local addr: {}", e)))?
.as_socket()
.ok_or_else(|| {
TransportError::StartFailed("local address is not an IP socket".into())
})?;
Ok(Self {
inner: sock,
local_addr,
})
}
pub fn local_addr(&self) -> SocketAddr {
self.local_addr
}
pub fn recv_buffer_size(&self) -> Result<usize, TransportError> {
self.inner
.recv_buffer_size()
.map_err(|e| TransportError::StartFailed(format!("get recv buffer: {}", e)))
}
pub fn send_buffer_size(&self) -> Result<usize, TransportError> {
self.inner
.send_buffer_size()
.map_err(|e| TransportError::StartFailed(format!("get send buffer: {}", e)))
}
pub fn into_async(self) -> Result<AsyncUdpSocket, TransportError> {
let std_socket: std::net::UdpSocket = self.inner.into();
let tokio_socket = tokio::net::UdpSocket::from_std(std_socket)
.map_err(|e| TransportError::StartFailed(format!("tokio socket failed: {}", e)))?;
Ok(AsyncUdpSocket {
inner: Arc::new(tokio_socket),
})
}
}
#[derive(Clone)]
pub struct AsyncUdpSocket {
inner: Arc<tokio::net::UdpSocket>,
}
impl AsyncUdpSocket {
pub async fn send_to(
&self,
data: &[u8],
dest: &SocketAddr,
) -> Result<usize, TransportError> {
match tokio::time::timeout(UDP_CONTROL_SEND_TIMEOUT, self.inner.send_to(data, dest))
.await
{
Ok(result) => {
result.map_err(|e| TransportError::SendFailed(format!("{}", e)))
}
Err(_) => Err(TransportError::Timeout),
}
}
pub async fn recv_from(
&self,
buf: &mut [u8],
) -> Result<(usize, SocketAddr, u32, usize), TransportError> {
let (n, addr) = self
.inner
.recv_from(buf)
.await
.map_err(|e| TransportError::RecvFailed(format!("{}", e)))?;
Ok((n, addr, 0, 0))
}
}
}
pub use platform::{AsyncUdpSocket, UdpRawSocket};
#[cfg(test)]
#[path = "socket/tests.rs"]
mod tests;