veecle_osal_std/net/
udp.rs

1//! UDP socket implementation for the std platform.
2
3use crate::IntoOsalError;
4use core::net::SocketAddr;
5use socket2::{Protocol, SockAddr, Type};
6use std::io::ErrorKind;
7use veecle_osal_api::net::udp::Error;
8
9/// UDP socket for sending and receiving datagrams.
10#[derive(Default)]
11pub struct UdpSocket {
12    /// The underlying socket.
13    ///
14    /// If this is `Some`, the socket is in use and cannot be used to `bind` or `send`.
15    socket: Option<tokio::net::UdpSocket>,
16}
17
18impl core::fmt::Debug for UdpSocket {
19    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
20        f.debug_struct("UdpSocket").finish()
21    }
22}
23
24impl UdpSocket {
25    /// Creates a new `UdpSocket`.
26    pub fn new() -> Self {
27        Self { socket: None }
28    }
29}
30
31impl veecle_osal_api::net::udp::UdpSocket for UdpSocket {
32    async fn bind(&mut self, address: SocketAddr) -> Result<(), Error> {
33        if self.socket.is_some() {
34            return Err(Error::InvalidState);
35        }
36
37        // We need to use `socket2ยด to set `SO_REUSEADDR` and `SO_REUSEPORT`,
38        // because neither Tokio nor the standard library support this
39        // for UDP sockets.
40
41        let socket2_socket = socket2::Socket::new(
42            socket2::Domain::for_address(address),
43            Type::DGRAM,
44            Some(Protocol::UDP),
45        )
46        .map_err(IntoOsalError::into_osal_error)?;
47
48        socket2_socket
49            .set_reuse_address(true)
50            .map_err(IntoOsalError::into_osal_error)?;
51        socket2_socket
52            .set_reuse_port(true)
53            .map_err(IntoOsalError::into_osal_error)?;
54        socket2_socket
55            .set_nonblocking(true)
56            .map_err(IntoOsalError::into_osal_error)?;
57
58        let socket2_addr: SockAddr = address.into();
59        socket2_socket
60            .bind(&socket2_addr)
61            .map_err(IntoOsalError::into_osal_error)?;
62
63        let tokio_socket = tokio::net::UdpSocket::from_std(socket2_socket.into())
64            .map_err(IntoOsalError::into_osal_error)?;
65
66        self.socket = Some(tokio_socket);
67        Ok(())
68    }
69
70    fn local_addr(&self) -> Result<SocketAddr, Error> {
71        self.socket
72            .as_ref()
73            .ok_or(Error::SocketNotBound)?
74            .local_addr()
75            .map_err(IntoOsalError::into_osal_error)
76    }
77
78    async fn recv_from(&self, buffer: &mut [u8]) -> Result<(usize, SocketAddr), Error> {
79        let Some(socket) = self.socket.as_ref() else {
80            return Err(Error::SocketNotBound);
81        };
82
83        let (read, address) = socket
84            .recv_from(buffer)
85            .await
86            .map_err(IntoOsalError::into_osal_error)?;
87
88        Ok((read, address))
89    }
90
91    async fn send_to(&self, buffer: &[u8], address: SocketAddr) -> Result<usize, Error> {
92        let Some(socket) = self.socket.as_ref() else {
93            return Err(Error::SocketNotBound);
94        };
95
96        let read = socket
97            .send_to(buffer, address)
98            .await
99            .map_err(IntoOsalError::into_osal_error)?;
100
101        Ok(read)
102    }
103
104    fn close(&mut self) {
105        self.socket = None;
106    }
107}
108
109impl IntoOsalError<Error> for std::io::Error {
110    fn into_osal_error(self) -> Error {
111        match self.kind() {
112            ErrorKind::PermissionDenied => Error::PermissionDenied,
113            ErrorKind::HostUnreachable => Error::NoRoute,
114            ErrorKind::NetworkUnreachable => Error::NoRoute,
115            ErrorKind::AddrInUse => Error::InvalidAddress,
116            ErrorKind::AddrNotAvailable => Error::InvalidAddress,
117            ErrorKind::NetworkDown => Error::NetworkDown,
118            _ => Error::Other,
119        }
120    }
121}