nex_socket/udp/
sync_impl.rs

1use crate::udp::UdpConfig;
2use socket2::{Domain, Protocol, Socket, Type as SockType};
3use std::io;
4use std::net::{SocketAddr, UdpSocket as StdUdpSocket};
5
6/// Synchronous low level UDP socket.
7#[derive(Debug)]
8pub struct UdpSocket {
9    socket: Socket,
10}
11
12impl UdpSocket {
13    /// Create a socket from the provided configuration.
14    pub fn from_config(config: &UdpConfig) -> io::Result<Self> {
15        let socket = Socket::new(
16            config.socket_family.to_domain(),
17            config.socket_type.to_sock_type(),
18            Some(Protocol::UDP),
19        )?;
20
21        socket.set_nonblocking(false)?;
22
23        // Set socket options based on configuration
24        if let Some(flag) = config.reuseaddr {
25            socket.set_reuse_address(flag)?;
26        }
27        if let Some(flag) = config.broadcast {
28            socket.set_broadcast(flag)?;
29        }
30        if let Some(ttl) = config.ttl {
31            socket.set_ttl(ttl)?;
32        }
33        if let Some(hoplimit) = config.hoplimit {
34            socket.set_unicast_hops_v6(hoplimit)?;
35        }
36        if let Some(timeout) = config.read_timeout {
37            socket.set_read_timeout(Some(timeout))?;
38        }
39        if let Some(timeout) = config.write_timeout {
40            socket.set_write_timeout(Some(timeout))?;
41        }
42
43        // Linux: optional interface name
44        #[cfg(any(target_os = "linux", target_os = "android", target_os = "fuchsia"))]
45        if let Some(iface) = &config.bind_device {
46            socket.bind_device(Some(iface.as_bytes()))?;
47        }
48
49        // bind to the specified address if provided
50        if let Some(addr) = config.bind_addr {
51            socket.bind(&addr.into())?;
52        }
53
54        Ok(Self { socket })
55    }
56
57    /// Create a socket of arbitrary type (DGRAM or RAW).
58    pub fn new(domain: Domain, sock_type: SockType) -> io::Result<Self> {
59        let socket = Socket::new(domain, sock_type, Some(Protocol::UDP))?;
60        socket.set_nonblocking(false)?;
61        Ok(Self { socket })
62    }
63
64    /// Convenience constructor for IPv4 DGRAM.
65    pub fn v4_dgram() -> io::Result<Self> {
66        Self::new(Domain::IPV4, SockType::DGRAM)
67    }
68
69    /// Convenience constructor for IPv6 DGRAM.
70    pub fn v6_dgram() -> io::Result<Self> {
71        Self::new(Domain::IPV6, SockType::DGRAM)
72    }
73
74    /// IPv4 RAW UDP. Requires administrator privileges.
75    pub fn raw_v4() -> io::Result<Self> {
76        Self::new(Domain::IPV4, SockType::RAW)
77    }
78
79    /// IPv6 RAW UDP. Requires administrator privileges.
80    pub fn raw_v6() -> io::Result<Self> {
81        Self::new(Domain::IPV6, SockType::RAW)
82    }
83
84    /// Send data.
85    pub fn send_to(&self, buf: &[u8], target: SocketAddr) -> io::Result<usize> {
86        self.socket.send_to(buf, &target.into())
87    }
88
89    /// Receive data.
90    pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
91        // Safety: `MaybeUninit<u8>` has the same layout as `u8`.
92        let buf_maybe = unsafe {
93            std::slice::from_raw_parts_mut(
94                buf.as_mut_ptr() as *mut std::mem::MaybeUninit<u8>,
95                buf.len(),
96            )
97        };
98
99        let (n, addr) = self.socket.recv_from(buf_maybe)?;
100        let addr = addr
101            .as_socket()
102            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "invalid address format"))?;
103
104        Ok((n, addr))
105    }
106
107    pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
108        self.socket.set_ttl(ttl)
109    }
110
111    pub fn set_hoplimit(&self, hops: u32) -> io::Result<()> {
112        self.socket.set_unicast_hops_v6(hops)
113    }
114
115    pub fn set_keepalive(&self, on: bool) -> io::Result<()> {
116        self.socket.set_keepalive(on)
117    }
118
119    /// Retrieve the local socket address.
120    pub fn local_addr(&self) -> io::Result<SocketAddr> {
121        self.socket
122            .local_addr()?
123            .as_socket()
124            .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Failed to get socket address"))
125    }
126
127    /// Convert into a raw `std::net::UdpSocket`.
128    pub fn to_std(self) -> io::Result<StdUdpSocket> {
129        Ok(self.socket.into())
130    }
131
132    #[cfg(unix)]
133    pub fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
134        use std::os::fd::AsRawFd;
135        self.socket.as_raw_fd()
136    }
137
138    #[cfg(windows)]
139    pub fn as_raw_socket(&self) -> std::os::windows::io::RawSocket {
140        use std::os::windows::io::AsRawSocket;
141        self.socket.as_raw_socket()
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148
149    #[test]
150    fn create_v4_socket() {
151        let sock = UdpSocket::v4_dgram().expect("create socket");
152        let addr = sock.local_addr().expect("addr");
153        assert!(addr.is_ipv4());
154    }
155}