Skip to main content

nex_socket/udp/
async_impl.rs

1use crate::udp::UdpConfig;
2use socket2::{Domain, Protocol, Socket, Type as SockType};
3use std::io;
4use std::net::{SocketAddr, UdpSocket as StdUdpSocket};
5use tokio::net::UdpSocket;
6
7/// Asynchronous UDP socket built on top of Tokio.
8#[derive(Debug)]
9pub struct AsyncUdpSocket {
10    inner: UdpSocket,
11}
12
13impl AsyncUdpSocket {
14    /// Create an asynchronous UDP socket from the given configuration.
15    pub fn from_config(config: &UdpConfig) -> io::Result<Self> {
16        config.validate()?;
17
18        let socket = Socket::new(
19            config.socket_family.to_domain(),
20            config.socket_type.to_sock_type(),
21            Some(Protocol::UDP),
22        )?;
23
24        socket.set_nonblocking(true)?;
25
26        // Set socket options based on configuration
27        if let Some(flag) = config.reuseaddr {
28            socket.set_reuse_address(flag)?;
29        }
30        #[cfg(any(
31            target_os = "android",
32            target_os = "dragonfly",
33            target_os = "freebsd",
34            target_os = "fuchsia",
35            target_os = "ios",
36            target_os = "linux",
37            target_os = "macos",
38            target_os = "netbsd",
39            target_os = "openbsd",
40            target_os = "tvos",
41            target_os = "visionos",
42            target_os = "watchos"
43        ))]
44        if let Some(flag) = config.reuseport {
45            socket.set_reuse_port(flag)?;
46        }
47        if let Some(flag) = config.broadcast {
48            socket.set_broadcast(flag)?;
49        }
50        if let Some(ttl) = config.ttl {
51            socket.set_ttl(ttl)?;
52        }
53        if let Some(hoplimit) = config.hoplimit {
54            socket.set_unicast_hops_v6(hoplimit)?;
55        }
56        if let Some(timeout) = config.read_timeout {
57            socket.set_read_timeout(Some(timeout))?;
58        }
59        if let Some(timeout) = config.write_timeout {
60            socket.set_write_timeout(Some(timeout))?;
61        }
62        if let Some(size) = config.recv_buffer_size {
63            socket.set_recv_buffer_size(size)?;
64        }
65        if let Some(size) = config.send_buffer_size {
66            socket.set_send_buffer_size(size)?;
67        }
68        if let Some(tos) = config.tos {
69            socket.set_tos(tos)?;
70        }
71        #[cfg(any(
72            target_os = "android",
73            target_os = "dragonfly",
74            target_os = "freebsd",
75            target_os = "fuchsia",
76            target_os = "ios",
77            target_os = "linux",
78            target_os = "macos",
79            target_os = "netbsd",
80            target_os = "openbsd",
81            target_os = "tvos",
82            target_os = "visionos",
83            target_os = "watchos"
84        ))]
85        if let Some(tclass) = config.tclass_v6 {
86            socket.set_tclass_v6(tclass)?;
87        }
88        if let Some(only_v6) = config.only_v6 {
89            socket.set_only_v6(only_v6)?;
90        }
91        if let Some(on) = config.recv_pktinfo {
92            crate::udp::set_recv_pktinfo(&socket, config.socket_family, on)?;
93        }
94
95        // Linux: optional interface name
96        #[cfg(any(target_os = "linux", target_os = "android", target_os = "fuchsia"))]
97        if let Some(iface) = &config.bind_device {
98            socket.bind_device(Some(iface.as_bytes()))?;
99        }
100
101        // bind to the specified address if provided
102        if let Some(addr) = config.bind_addr {
103            socket.bind(&addr.into())?;
104        }
105
106        #[cfg(windows)]
107        let std_socket = unsafe {
108            use std::os::windows::io::{FromRawSocket, IntoRawSocket};
109            StdUdpSocket::from_raw_socket(socket.into_raw_socket())
110        };
111        #[cfg(unix)]
112        let std_socket = unsafe {
113            use std::os::fd::{FromRawFd, IntoRawFd};
114            StdUdpSocket::from_raw_fd(socket.into_raw_fd())
115        };
116
117        let inner = UdpSocket::from_std(std_socket)?;
118
119        Ok(Self { inner })
120    }
121
122    /// Create a socket of arbitrary type (DGRAM or RAW).
123    pub fn new(domain: Domain, sock_type: SockType) -> io::Result<Self> {
124        let socket = Socket::new(domain, sock_type, Some(Protocol::UDP))?;
125        socket.set_nonblocking(true)?;
126
127        #[cfg(windows)]
128        let std_socket = unsafe {
129            use std::os::windows::io::{FromRawSocket, IntoRawSocket};
130            StdUdpSocket::from_raw_socket(socket.into_raw_socket())
131        };
132        #[cfg(unix)]
133        let std_socket = unsafe {
134            use std::os::fd::{FromRawFd, IntoRawFd};
135            StdUdpSocket::from_raw_fd(socket.into_raw_fd())
136        };
137
138        let inner = UdpSocket::from_std(std_socket)?;
139
140        Ok(Self { inner })
141    }
142
143    /// Convenience constructor for IPv4 DGRAM.
144    pub fn v4_dgram() -> io::Result<Self> {
145        Self::new(Domain::IPV4, SockType::DGRAM)
146    }
147
148    /// Convenience constructor for IPv6 DGRAM.
149    pub fn v6_dgram() -> io::Result<Self> {
150        Self::new(Domain::IPV6, SockType::DGRAM)
151    }
152
153    /// IPv4 RAW UDP. Requires administrator privileges.
154    pub fn raw_v4() -> io::Result<Self> {
155        Self::new(Domain::IPV4, SockType::RAW)
156    }
157
158    /// IPv6 RAW UDP. Requires administrator privileges.
159    pub fn raw_v6() -> io::Result<Self> {
160        Self::new(Domain::IPV6, SockType::RAW)
161    }
162
163    /// Send data asynchronously.
164    pub async fn send_to(&self, buf: &[u8], target: SocketAddr) -> io::Result<usize> {
165        self.inner.send_to(buf, target).await
166    }
167
168    /// Receive data asynchronously.
169    pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
170        self.inner.recv_from(buf).await
171    }
172
173    /// Retrieve the local socket address.
174    pub fn local_addr(&self) -> io::Result<SocketAddr> {
175        self.inner.local_addr()
176    }
177
178    pub fn into_tokio_socket(self) -> io::Result<UdpSocket> {
179        Ok(self.inner)
180    }
181
182    /// Construct from a standard UDP socket.
183    pub fn from_std_socket(socket: StdUdpSocket) -> io::Result<Self> {
184        Ok(Self {
185            inner: UdpSocket::from_std(socket)?,
186        })
187    }
188
189    /// Convert into a standard UDP socket.
190    pub fn into_std_socket(self) -> io::Result<StdUdpSocket> {
191        self.inner.into_std()
192    }
193
194    /// Set IPv4 time-to-live.
195    pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
196        self.inner.set_ttl(ttl)
197    }
198
199    /// Get IPv4 time-to-live.
200    pub fn ttl(&self) -> io::Result<u32> {
201        self.inner.ttl()
202    }
203
204    /// Set broadcast mode.
205    pub fn set_broadcast(&self, on: bool) -> io::Result<()> {
206        self.inner.set_broadcast(on)
207    }
208
209    /// Get broadcast mode.
210    pub fn broadcast(&self) -> io::Result<bool> {
211        self.inner.broadcast()
212    }
213
214    #[cfg(unix)]
215    pub fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
216        use std::os::fd::AsRawFd;
217        self.inner.as_raw_fd()
218    }
219
220    #[cfg(windows)]
221    pub fn as_raw_socket(&self) -> std::os::windows::io::RawSocket {
222        use std::os::windows::io::AsRawSocket;
223        self.inner.as_raw_socket()
224    }
225}