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        // Determine address family from the bind address
16        let domain = match config.bind_addr {
17            Some(SocketAddr::V4(_)) => Domain::IPV4,
18            Some(SocketAddr::V6(_)) => Domain::IPV6,
19            None => Domain::IPV4, // default
20        };
21
22        let socket = Socket::new(domain, SockType::DGRAM, Some(Protocol::UDP))?;
23
24        if let Some(flag) = config.reuseaddr {
25            socket.set_reuse_address(flag)?;
26        }
27
28        if let Some(flag) = config.broadcast {
29            socket.set_broadcast(flag)?;
30        }
31
32        if let Some(ttl) = config.ttl {
33            socket.set_ttl(ttl)?;
34        }
35
36        #[cfg(any(target_os = "linux", target_os = "android", target_os = "fuchsia"))]
37        if let Some(iface) = &config.bind_device {
38            socket.bind_device(Some(iface.as_bytes()))?;
39        }
40
41        if let Some(addr) = config.bind_addr {
42            socket.bind(&addr.into())?;
43        }
44
45        socket.set_nonblocking(false)?; // blocking mode for sync usage
46        Ok(Self { socket })
47    }
48
49    /// Create a socket of arbitrary type (DGRAM or RAW).
50    pub fn new(domain: Domain, sock_type: SockType) -> io::Result<Self> {
51        let socket = Socket::new(domain, sock_type, Some(Protocol::UDP))?;
52        socket.set_nonblocking(false)?;
53        Ok(Self { socket })
54    }
55
56    /// Convenience constructor for IPv4 DGRAM.
57    pub fn v4_dgram() -> io::Result<Self> {
58        Self::new(Domain::IPV4, SockType::DGRAM)
59    }
60
61    /// Convenience constructor for IPv6 DGRAM.
62    pub fn v6_dgram() -> io::Result<Self> {
63        Self::new(Domain::IPV6, SockType::DGRAM)
64    }
65
66    /// IPv4 RAW UDP. Requires administrator privileges.
67    pub fn raw_v4() -> io::Result<Self> {
68        Self::new(Domain::IPV4, SockType::RAW)
69    }
70
71    /// IPv6 RAW UDP. Requires administrator privileges.
72    pub fn raw_v6() -> io::Result<Self> {
73        Self::new(Domain::IPV6, SockType::RAW)
74    }
75
76    /// Send data.
77    pub fn send_to(&self, buf: &[u8], target: SocketAddr) -> io::Result<usize> {
78        self.socket.send_to(buf, &target.into())
79    }
80
81    /// Receive data.
82    pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
83        // Safety: `MaybeUninit<u8>` has the same layout as `u8`.
84        let buf_maybe = unsafe {
85            std::slice::from_raw_parts_mut(
86                buf.as_mut_ptr() as *mut std::mem::MaybeUninit<u8>,
87                buf.len(),
88            )
89        };
90
91        let (n, addr) = self.socket.recv_from(buf_maybe)?;
92        let addr = addr
93            .as_socket()
94            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "invalid address format"))?;
95
96        Ok((n, addr))
97    }
98
99    /// Retrieve the local socket address.
100    pub fn local_addr(&self) -> io::Result<SocketAddr> {
101        self.socket
102            .local_addr()?
103            .as_socket()
104            .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Failed to get socket address"))
105    }
106
107    /// Convert into a raw `std::net::UdpSocket`.
108    pub fn to_std(self) -> io::Result<StdUdpSocket> {
109        Ok(self.socket.into())
110    }
111
112    #[cfg(unix)]
113    pub fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
114        use std::os::fd::AsRawFd;
115        self.socket.as_raw_fd()
116    }
117
118    #[cfg(windows)]
119    pub fn as_raw_socket(&self) -> std::os::windows::io::RawSocket {
120        use std::os::windows::io::AsRawSocket;
121        self.socket.as_raw_socket()
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128
129    #[test]
130    fn create_v4_socket() {
131        let sock = UdpSocket::v4_dgram().expect("create socket");
132        let addr = sock.local_addr().expect("addr");
133        assert!(addr.is_ipv4());
134    }
135}