Skip to main content

veecle_osal_api/net/
udp.rs

1//! UDP socket abstractions.
2
3use core::fmt::Display;
4use core::fmt::Formatter;
5use core::net::SocketAddr;
6
7/// UDP socket for sending and receiving datagrams.
8///
9/// UDP is connectionless - each send/receive operation can target different addresses.
10///
11/// # Example
12///
13/// ```no_run
14/// use veecle_osal_api::net::udp::UdpSocket;
15/// use core::net::SocketAddr;
16///
17/// async fn udp_echo<S>(mut socket: S)
18/// where
19///     S: UdpSocket
20/// {
21///     let addr: SocketAddr = "0.0.0.0:8080".parse().unwrap();
22///     socket.bind(addr).await.unwrap();
23///
24///     let mut buffer = [0u8; 1500];
25///     loop {
26///         let (size, peer) = socket.recv_from(&mut buffer).await.unwrap();
27///         socket.send_to(&buffer[..size], peer).await.unwrap();
28///     }
29/// }
30/// ```
31#[expect(async_fn_in_trait)]
32pub trait UdpSocket {
33    /// Binds the socket to a local address.
34    ///
35    /// If the specified port is `0`, the port is assigned automatically and can be queried with [`Self::local_addr`].
36    async fn bind(&mut self, address: SocketAddr) -> Result<(), Error>;
37
38    /// Returns the local address this socket is bound to.
39    fn local_addr(&self) -> Result<SocketAddr, Error>;
40
41    /// Receives a datagram.
42    ///
43    /// Returns the number of bytes received and the sender's address.
44    /// If the datagram is larger than the buffer, excess bytes may be discarded.
45    async fn recv_from(&self, buffer: &mut [u8]) -> Result<(usize, SocketAddr), Error>;
46
47    /// Sends a datagram to the specified address.
48    ///
49    /// Returns the number of bytes sent.
50    async fn send_to(&self, buffer: &[u8], address: SocketAddr) -> Result<usize, Error>;
51
52    /// Closes the UDP socket.
53    fn close(&mut self);
54}
55
56/// Errors that can occur when using UDP sockets.
57#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash)]
58pub enum Error {
59    /// The provided buffer was too small for the received datagram.
60    ///
61    /// The datagram may have been dropped.
62    BufferTooSmall,
63    /// The provided buffer was too large for internal buffer.
64    BufferTooLarge,
65    /// The socket is in an invalid state.
66    InvalidState,
67    /// No permission to access the resource.
68    PermissionDenied,
69    /// No route to host.
70    NoRoute,
71    /// The provided port is invalid.
72    InvalidPort,
73    /// The provided address is invalid.
74    ///
75    /// This can occur if the address is already in use or doesn't exist.
76    InvalidAddress,
77    /// The network stack is down.
78    NetworkDown,
79    /// The socket is not bound to an outgoing address and port.
80    SocketNotBound,
81    /// Currently unhandled error occurred.
82    /// Please open a bug report if you encounter this error.
83    Other,
84}
85
86impl Display for Error {
87    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
88        match self {
89            Error::InvalidState => {
90                write!(f, "The socket is in an invalid state.")
91            }
92            Error::BufferTooSmall => {
93                write!(
94                    f,
95                    "The provided buffer was too small for the received datagram."
96                )
97            }
98            Error::BufferTooLarge => {
99                write!(f, "The provided buffer was too large for internal buffer.")
100            }
101            Error::NoRoute => {
102                write!(f, "No route to host.")
103            }
104            Error::PermissionDenied => {
105                write!(f, "No permission to access the resource.")
106            }
107            Error::InvalidPort => {
108                write!(f, "The provided port is invalid.")
109            }
110            Error::InvalidAddress => {
111                write!(f, "The provided address is invalid.")
112            }
113            Error::NetworkDown => {
114                write!(f, "The network stack is down.")
115            }
116            Error::Other => {
117                write!(
118                    f,
119                    "Unspecified error, please open a bug report if you encounter this error."
120                )
121            }
122            Error::SocketNotBound => {
123                write!(
124                    f,
125                    "The socket is not bound to an outgoing address and port."
126                )
127            }
128        }
129    }
130}
131
132impl core::error::Error for Error {}
133
134#[doc(hidden)]
135#[cfg(feature = "test-suites")]
136#[cfg_attr(coverage_nightly, coverage(off))]
137pub mod test_suite {
138    //! Test suite for UDP sockets.
139
140    use crate::net::udp::{Error, UdpSocket};
141    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
142
143    pub async fn test_bind_all_zero_address_v4(mut socket: impl UdpSocket) {
144        let address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0);
145
146        socket.bind(address).await.unwrap();
147
148        let bound_addr = socket.local_addr().unwrap();
149
150        assert_eq!(bound_addr.ip(), IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED));
151
152        assert_ne!(
153            bound_addr.port(),
154            0,
155            "port should be automatically assigned"
156        );
157
158        socket.close();
159    }
160
161    pub async fn test_bind_all_zero_address_v6(mut socket: impl UdpSocket) {
162        let address = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), 0);
163
164        socket.bind(address).await.unwrap();
165
166        let bound_addr = socket.local_addr().unwrap();
167
168        assert_eq!(bound_addr.ip(), IpAddr::V6(std::net::Ipv6Addr::UNSPECIFIED));
169
170        assert_ne!(
171            bound_addr.port(),
172            0,
173            "port should be automatically assigned"
174        );
175
176        socket.close();
177    }
178
179    pub async fn test_bind_specific_port(mut socket: impl UdpSocket, ip_address: &str) {
180        let ip_address = ip_address.parse().unwrap();
181        let address = SocketAddr::new(ip_address, 58080);
182
183        socket.bind(address).await.unwrap();
184
185        let bound_addr = socket.local_addr().unwrap();
186
187        assert_eq!(bound_addr.ip(), ip_address);
188        assert_eq!(bound_addr.port(), 58080, "port should match requested port");
189
190        socket.close();
191    }
192
193    pub async fn test_send_recv(
194        mut socket1: impl UdpSocket,
195        mut socket2: impl UdpSocket,
196        ip_address: &str,
197    ) {
198        let ip_address = ip_address.parse().unwrap();
199        let addr1 = SocketAddr::new(ip_address, 58081);
200        let addr2 = SocketAddr::new(ip_address, 58082);
201
202        socket1.bind(addr1).await.unwrap();
203        socket2.bind(addr2).await.unwrap();
204
205        let send_data = b"Hello, UDP!";
206        let mut recv_buffer = [0u8; 64];
207
208        let sent = socket1.send_to(send_data, addr2).await.unwrap();
209        assert_eq!(sent, send_data.len());
210
211        let (received, sender_addr) = socket2.recv_from(&mut recv_buffer).await.unwrap();
212        assert_eq!(received, send_data.len());
213        assert_eq!(&recv_buffer[..received], send_data);
214        assert_eq!(sender_addr, addr1);
215
216        let response = b"Hello back!";
217        let sent = socket2.send_to(response, addr1).await.unwrap();
218        assert_eq!(sent, response.len());
219
220        let (received, sender_addr) = socket1.recv_from(&mut recv_buffer).await.unwrap();
221        assert_eq!(received, response.len());
222        assert_eq!(&recv_buffer[..received], response);
223        assert_eq!(sender_addr, addr2);
224
225        socket1.close();
226        socket2.close();
227    }
228
229    pub async fn test_local_addr_before_bind(socket: impl UdpSocket) {
230        assert_eq!(socket.local_addr(), Err(Error::SocketNotBound));
231    }
232
233    pub async fn test_close_socket(mut socket: impl UdpSocket, ip_address: &str) {
234        let ip_address = ip_address.parse().unwrap();
235        let address = SocketAddr::new(ip_address, 58085);
236
237        socket.bind(address).await.unwrap();
238        let bound_addr = socket.local_addr().unwrap();
239        assert_eq!(bound_addr, address);
240
241        socket.close();
242
243        assert_eq!(socket.local_addr(), Err(Error::SocketNotBound));
244    }
245
246    pub async fn test_recv_without_bind(socket: impl UdpSocket) {
247        let mut buffer = [0u8; 64];
248        assert_eq!(
249            socket.recv_from(&mut buffer).await,
250            Err(Error::SocketNotBound)
251        );
252    }
253
254    pub async fn test_send_without_bind(socket: impl UdpSocket, ip_address: &str) {
255        let ip_address = ip_address.parse().unwrap();
256        let target_addr = SocketAddr::new(ip_address, 58086);
257        let data = b"test data";
258
259        assert_eq!(
260            socket.send_to(data, target_addr).await,
261            Err(Error::SocketNotBound)
262        );
263    }
264
265    pub async fn test_bind_multiple_sockets_same_ip(
266        mut socket1: impl UdpSocket,
267        mut socket2: impl UdpSocket,
268        ip_address: &str,
269    ) {
270        let ip_address = ip_address.parse().unwrap();
271        let address = SocketAddr::new(ip_address, 58087);
272
273        socket1.bind(address).await.unwrap();
274        socket2.bind(address).await.unwrap();
275    }
276
277    pub async fn test_zero_length_datagram(
278        mut socket1: impl UdpSocket,
279        mut socket2: impl UdpSocket,
280        ip_address: &str,
281    ) {
282        let ip_address = ip_address.parse().unwrap();
283        let addr1 = SocketAddr::new(ip_address, 58088);
284        let addr2 = SocketAddr::new(ip_address, 58089);
285
286        socket1.bind(addr1).await.unwrap();
287        socket2.bind(addr2).await.unwrap();
288
289        let empty_data = &[];
290        let sent = socket1.send_to(empty_data, addr2).await.unwrap();
291        assert_eq!(sent, 0);
292
293        let mut recv_buffer = [0u8; 64];
294        let (received, sender_addr) = socket2.recv_from(&mut recv_buffer).await.unwrap();
295        assert_eq!(received, 0);
296        assert_eq!(sender_addr, addr1);
297
298        socket1.close();
299        socket2.close();
300    }
301
302    pub async fn test_max_datagram_size(
303        mut socket1: impl UdpSocket,
304        mut socket2: impl UdpSocket,
305        ip_address: &str,
306    ) {
307        let ip_address = ip_address.parse().unwrap();
308        let addr1 = SocketAddr::new(ip_address, 58090);
309        let addr2 = SocketAddr::new(ip_address, 58091);
310
311        socket1.bind(addr1).await.unwrap();
312        socket2.bind(addr2).await.unwrap();
313
314        // Test maximum practical UDP payload size.
315        // 65507 = 65535 - 8 (UDP header) - 20 (IPv4 header).
316        const MAX_SIZE: usize = 65507;
317        let mut send_data = [0u8; MAX_SIZE];
318        for (i, byte) in send_data.iter_mut().enumerate() {
319            *byte = (i % 256) as u8;
320        }
321
322        let sent = socket1.send_to(&send_data, addr2).await.unwrap();
323        assert_eq!(sent, MAX_SIZE);
324
325        let mut recv_buffer = [0u8; MAX_SIZE];
326        let (received, sender_addr) = socket2.recv_from(&mut recv_buffer).await.unwrap();
327        assert_eq!(received, MAX_SIZE);
328        assert_eq!(&recv_buffer[..], &send_data[..]);
329        assert_eq!(sender_addr, addr1);
330
331        socket1.close();
332        socket2.close();
333    }
334
335    pub async fn test_multiple_binds(mut socket: impl UdpSocket, ip_address: &str) {
336        let ip_address = ip_address.parse().unwrap();
337        let addr1 = SocketAddr::new(ip_address, 58092);
338        let addr2 = SocketAddr::new(ip_address, 58093);
339
340        socket.bind(addr1).await.unwrap();
341        assert_eq!(socket.local_addr().unwrap(), addr1);
342
343        assert_eq!(socket.bind(addr2).await, Err(Error::InvalidState));
344
345        socket.close();
346    }
347
348    pub async fn test_rebind_after_close(mut socket: impl UdpSocket, ip_address: &str) {
349        let ip_address = ip_address.parse().unwrap();
350        let addr1 = SocketAddr::new(ip_address, 58094);
351        let addr2 = SocketAddr::new(ip_address, 58095);
352
353        socket.bind(addr1).await.unwrap();
354        assert_eq!(socket.local_addr().unwrap(), addr1);
355
356        socket.close();
357
358        assert_eq!(socket.local_addr(), Err(Error::SocketNotBound));
359
360        socket.bind(addr2).await.unwrap();
361        assert_eq!(socket.local_addr().unwrap(), addr2);
362
363        socket.close();
364    }
365}