Skip to main content

ts_netstack_smoltcp_socket/
udp.rs

1use core::{
2    fmt::{Debug, Formatter},
3    net::SocketAddr,
4    num::NonZeroUsize,
5};
6
7use bytes::Bytes;
8use netcore::{HasChannel, Response, smoltcp::iface::SocketHandle};
9
10use crate::netcore::{DisplayExt, udp};
11
12/// A UDP socket.
13pub struct UdpSocket {
14    pub(crate) sender: netcore::Channel,
15    pub(crate) handle: SocketHandle,
16
17    pub(crate) local: SocketAddr,
18}
19
20impl Debug for UdpSocket {
21    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
22        f.debug_struct("UdpSocket")
23            .field("handle", &self.handle.as_display_debug())
24            .field("local_endpoint", &self.local)
25            .finish()
26    }
27}
28
29impl UdpSocket {
30    /// Send a packet to the given endpoint with the provided data.
31    pub fn send_to_blocking(
32        &self,
33        endpoint: SocketAddr,
34        data: &[u8],
35    ) -> Result<(), netcore::Error> {
36        self.request_blocking(udp::Command::Send {
37            endpoint,
38            local: None,
39            buf: Bytes::copy_from_slice(data),
40        })?
41        .to_ok()
42    }
43
44    /// Send a packet to the given endpoint with the provided data.
45    pub async fn send_to(&self, endpoint: SocketAddr, data: &[u8]) -> Result<(), netcore::Error> {
46        self.request(udp::Command::Send {
47            endpoint,
48            local: None,
49            buf: Bytes::copy_from_slice(data),
50        })
51        .await?
52        .to_ok()
53    }
54
55    /// Send a packet to `endpoint`, spoofing the source address as `local`.
56    ///
57    /// Used by a forwarder so reply datagrams appear to originate from the original
58    /// destination the peer expected.
59    pub fn send_to_from_blocking(
60        &self,
61        endpoint: SocketAddr,
62        local: core::net::IpAddr,
63        data: &[u8],
64    ) -> Result<(), netcore::Error> {
65        self.request_blocking(udp::Command::Send {
66            endpoint,
67            local: Some(local),
68            buf: Bytes::copy_from_slice(data),
69        })?
70        .to_ok()
71    }
72
73    /// Send a packet to `endpoint`, spoofing the source address as `local`.
74    ///
75    /// Used by a forwarder so reply datagrams appear to originate from the original
76    /// destination the peer expected.
77    pub async fn send_to_from(
78        &self,
79        endpoint: SocketAddr,
80        local: core::net::IpAddr,
81        data: &[u8],
82    ) -> Result<(), netcore::Error> {
83        self.request(udp::Command::Send {
84            endpoint,
85            local: Some(local),
86            buf: Bytes::copy_from_slice(data),
87        })
88        .await?
89        .to_ok()
90    }
91
92    /// Receive a packet into the given buffer.
93    pub fn recv_from_blocking(
94        &self,
95        buf: &mut [u8],
96    ) -> Result<(SocketAddr, usize), netcore::Error> {
97        let len = NonZeroUsize::new(buf.len()).ok_or(netcore::Error::zero_buffer())?;
98
99        let resp = self.request_blocking(udp::Command::Recv { max_len: Some(len) })?;
100
101        self._udp_recv(resp, buf)
102    }
103
104    /// Receive a packet bytes buffer.
105    pub fn recv_from_bytes_blocking(&self) -> Result<(SocketAddr, Bytes), netcore::Error> {
106        let resp = self.request_blocking(udp::Command::Recv { max_len: None })?;
107        self._udp_recv_bytes(resp)
108    }
109
110    /// Receive a packet into the given buffer.
111    pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(SocketAddr, usize), netcore::Error> {
112        let len = NonZeroUsize::new(buf.len()).ok_or(netcore::Error::zero_buffer())?;
113
114        let resp = self
115            .request(udp::Command::Recv { max_len: Some(len) })
116            .await?;
117
118        self._udp_recv(resp, buf)
119    }
120
121    /// Asynchronously receive a packet bytes buffer.
122    pub async fn recv_from_bytes(&self) -> Result<(SocketAddr, Bytes), netcore::Error> {
123        let resp = self.request(udp::Command::Recv { max_len: None }).await?;
124
125        self._udp_recv_bytes(resp)
126    }
127
128    /// Receive a packet, also reporting the local (destination) address it was sent to.
129    ///
130    /// Under any-IP acceptance the local address is the original packet destination -- a
131    /// forwarder needs it to know which real socket to relay through. Returns
132    /// `(remote, local, payload)`.
133    pub fn recv_from_with_dst_bytes_blocking(
134        &self,
135    ) -> Result<(SocketAddr, SocketAddr, Bytes), netcore::Error> {
136        let resp = self.request_blocking(udp::Command::Recv { max_len: None })?;
137        self._udp_recv_with_dst_bytes(resp)
138    }
139
140    /// Receive a packet, also reporting the local (destination) address it was sent to.
141    ///
142    /// Under any-IP acceptance the local address is the original packet destination -- a
143    /// forwarder needs it to know which real socket to relay through. Returns
144    /// `(remote, local, payload)`.
145    pub async fn recv_from_with_dst_bytes(
146        &self,
147    ) -> Result<(SocketAddr, SocketAddr, Bytes), netcore::Error> {
148        let resp = self.request(udp::Command::Recv { max_len: None }).await?;
149        self._udp_recv_with_dst_bytes(resp)
150    }
151
152    fn _udp_recv(
153        &self,
154        resp: Response,
155        buf: &mut [u8],
156    ) -> Result<(SocketAddr, usize), netcore::Error> {
157        let (remote, ret) = self._udp_recv_bytes(resp)?;
158
159        debug_assert!(ret.len() <= buf.len());
160        let n_copied = ret.len().min(buf.len());
161
162        buf[..n_copied].copy_from_slice(&ret[..n_copied]);
163
164        Ok((remote, n_copied))
165    }
166
167    fn _udp_recv_bytes(&self, resp: Response) -> Result<(SocketAddr, Bytes), netcore::Error> {
168        let (remote, _local, ret) = self._udp_recv_with_dst_bytes(resp)?;
169        Ok((remote, ret))
170    }
171
172    fn _udp_recv_with_dst_bytes(
173        &self,
174        resp: Response,
175    ) -> Result<(SocketAddr, SocketAddr, Bytes), netcore::Error> {
176        netcore::try_response_as!(
177            resp,
178            udp::Response::RecvFrom {
179                remote,
180                local,
181                buf: ret,
182                truncated,
183            }
184        );
185
186        if let Some(truncated) = truncated {
187            tracing::warn!(truncated, "udp recv truncated");
188        }
189
190        Ok((remote, local, ret))
191    }
192
193    /// Report the local endpoint to which this socket is bound.
194    pub const fn local_addr(&self) -> SocketAddr {
195        self.local
196    }
197
198    socket_requestor_impl!();
199}
200
201impl Drop for UdpSocket {
202    fn drop(&mut self) {
203        if let Err(e) = self
204            .sender
205            .request_nonblocking(Some(self.handle), udp::Command::Close)
206        {
207            tracing::warn!(err = %e, "possible socket leak");
208        }
209    }
210}