1use std::{
2 future::Future,
3 io::{self, IoSlice, IoSliceMut},
4 net::SocketAddr,
5 num::NonZeroU32,
6 pin::Pin,
7 sync::atomic::AtomicI32,
8 task::{Context, Poll, ready},
9};
10
11use bytes::BytesMut;
12use qbase::net::route::Line;
13use socket2::{Domain, Socket, Type};
14use tokio::io::Interest;
15pub const BATCH_SIZE: usize = 64;
16cfg_if::cfg_if! {
17 if #[cfg(unix)]{
18 #[path = "unix.rs"]
19 mod unix;
20 } else if #[cfg(windows)] {
21 #[path = "windows.rs"]
22 mod windows;
23 } else {
24 compile_error!("Unsupported platform");
25 }
26}
27
28pub mod ext;
29
30#[derive(Debug)]
31pub struct UdpSocket {
32 io: tokio::net::UdpSocket,
33 ttl: AtomicI32,
34 bound_device: Option<BoundDevice>,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct BoundDevice {
39 name: String,
40 index: NonZeroU32,
41}
42
43impl BoundDevice {
44 pub fn new(name: impl Into<String>, index: u32) -> io::Result<Self> {
45 let index = NonZeroU32::new(index).ok_or_else(|| {
46 io::Error::new(
47 io::ErrorKind::InvalidInput,
48 "interface index must be non-zero",
49 )
50 })?;
51 Ok(Self {
52 name: name.into(),
53 index,
54 })
55 }
56
57 pub fn name(&self) -> &str {
58 &self.name
59 }
60
61 pub fn index(&self) -> NonZeroU32 {
62 self.index
63 }
64}
65
66impl UdpSocket {
67 pub fn bind(addr: SocketAddr) -> io::Result<Self> {
68 Self::bind_scoped(addr, None)
69 }
70
71 pub fn bind_to_device(addr: SocketAddr, device: BoundDevice) -> io::Result<Self> {
72 Self::bind_scoped(addr, Some(device))
73 }
74
75 fn bind_scoped(addr: SocketAddr, bound_device: Option<BoundDevice>) -> io::Result<Self> {
76 let domain = if addr.is_ipv4() {
77 Domain::IPV4
78 } else {
79 Domain::IPV6
80 };
81
82 let socket = Socket::new(domain, Type::DGRAM, None)?;
83 socket.set_nonblocking(true)?;
84 Self::config(&socket, addr)?;
85 if let Some(device) = bound_device.as_ref() {
86 let socket_ref = socket2::SockRef::from(&socket);
87 Self::bind_device_to_socket(&socket_ref, addr, device)?;
88 }
89 let io = tokio::net::UdpSocket::from_std(socket.into())?;
90 let usc = Self {
91 io,
92 ttl: AtomicI32::new(Line::DEFAULT_TTL as i32),
93 bound_device,
94 };
95 Ok(usc)
96 }
97
98 pub fn local_addr(&self) -> io::Result<SocketAddr> {
99 self.io.local_addr()
100 }
101
102 pub fn bound_device(&self) -> Option<&BoundDevice> {
103 self.bound_device.as_ref()
104 }
105
106 pub fn poll_send_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
107 self.io.poll_send_ready(cx)
108 }
109
110 pub fn poll_recv_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
111 self.io.poll_recv_ready(cx)
112 }
113
114 pub fn poll_send(
115 &self,
116 cx: &mut Context<'_>,
117 bufs: &[IoSlice<'_>],
118 line: &Line,
119 ) -> Poll<io::Result<usize>> {
120 loop {
121 ready!(self.poll_send_ready(cx))?;
122 self.set_ttl(line.ttl as i32)?;
123 match self
124 .io
125 .try_io(Interest::WRITABLE, || self.sendmsg(bufs, line))
126 {
127 Ok(n) => return Poll::Ready(Ok(n)),
128 Err(e) if e.kind() == io::ErrorKind::WouldBlock => continue,
129 Err(e) => return Poll::Ready(Err(e)),
130 }
131 }
132 }
133
134 pub fn poll_recv(
135 &self,
136 cx: &mut Context,
137 bufs: &mut [IoSliceMut<'_>],
138 lines: &mut [Line],
139 ) -> Poll<io::Result<usize>> {
140 loop {
141 ready!(self.poll_recv_ready(cx)?);
142 let f = || self.recvmsg(bufs, lines);
143 let ret = self.io.try_io(Interest::READABLE, f);
144 if matches!(&ret, Err(e) if e.kind() == io::ErrorKind::WouldBlock) {
145 continue;
146 } else {
147 return Poll::Ready(ret);
148 }
149 }
150 }
151
152 pub fn bind_device(&self, device: &str) -> io::Result<()> {
153 #[cfg(not(unix))]
154 {
155 let _ = device;
156 return Err(io::Error::new(
157 io::ErrorKind::Unsupported,
158 "binding an existing UDP socket by interface name is unsupported on this platform",
159 ));
160 }
161 #[cfg(unix)]
162 {
163 let index = nix::net::if_::if_nametoindex(device)?;
164 let device = BoundDevice::new(device, index)?;
165 let socket = socket2::SockRef::from(&self.io);
166 Self::bind_device_to_socket(&socket, self.io.local_addr()?, &device)
167 }
168 }
169}
170
171pub trait Io {
172 fn config(io: &socket2::Socket, addr: SocketAddr) -> io::Result<()>;
173
174 fn bind_device_to_socket(
175 io: &socket2::SockRef<'_>,
176 addr: SocketAddr,
177 device: &BoundDevice,
178 ) -> io::Result<()>;
179
180 fn sendmsg(&self, bufs: &[IoSlice<'_>], line: &Line) -> io::Result<usize>;
181
182 fn recvmsg(&self, bufs: &mut [IoSliceMut<'_>], line: &mut [Line]) -> io::Result<usize>;
183
184 fn set_ttl(&self, ttl: i32) -> io::Result<()>;
185}
186
187impl UdpSocket {
188 pub fn send<'a>(&'a self, iovecs: &'a [IoSlice<'a>], line: Line) -> Send<'a> {
189 Send {
190 socket: self,
191 iovecs,
192 line,
193 }
194 }
195
196 pub fn receive<'a>(&'a self, iovecs: &'a mut [BytesMut], lines: &'a mut [Line]) -> Receive<'a> {
197 Receive {
198 socket: self,
199 iovecs,
200 lines,
201 }
202 }
203}
204
205pub struct Send<'a> {
206 socket: &'a UdpSocket,
207 iovecs: &'a [IoSlice<'a>],
208 line: Line,
209}
210
211impl Future for Send<'_> {
212 type Output = io::Result<usize>;
213
214 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
215 let this = self.get_mut();
216 this.socket.poll_send(cx, this.iovecs, &this.line)
217 }
218}
219
220pub struct Receive<'a> {
221 socket: &'a UdpSocket,
222 iovecs: &'a mut [BytesMut],
223 lines: &'a mut [Line],
224}
225
226impl Future for Receive<'_> {
227 type Output = io::Result<usize>;
228
229 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
230 let this = self.get_mut();
231 let mut bufs = this
232 .iovecs
233 .iter_mut()
234 .map(|b| IoSliceMut::new(b))
235 .collect::<Vec<_>>();
236 this.socket.poll_recv(cx, &mut bufs, this.lines)
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use std::{
243 io,
244 net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
245 time::Duration,
246 };
247
248 use super::*;
249
250 #[tokio::test(flavor = "current_thread")]
251 async fn ipv6_wildcard_socket_does_not_receive_ipv4_packets() -> io::Result<()> {
252 let socket4 = UdpSocket::bind(SocketAddr::V6(SocketAddrV6::new(
253 Ipv6Addr::UNSPECIFIED,
254 0,
255 0,
256 0,
257 )))?;
258 let port = socket4.local_addr()?.port();
259
260 let socket6 =
261 std::net::UdpSocket::bind(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0)))?;
262 socket6.send_to(
263 b"ping",
264 SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, port)),
265 )?;
266
267 let mut iovecs = [BytesMut::with_capacity(1500); 1];
268 let mut lines = [Line::default(); 1];
269 let result = tokio::time::timeout(
270 Duration::from_millis(200),
271 socket4.receive(&mut iovecs, &mut lines),
272 )
273 .await;
274 assert!(
275 result.is_err(),
276 "unexpected ipv4 datagram arrived on ipv6 wildcard socket: {result:?}"
277 );
278 Ok(())
279 }
280
281 #[tokio::test(flavor = "current_thread")]
282 async fn ipv4_and_ipv6_wildcard_sockets_can_bind_same_port() -> io::Result<()> {
283 let v6 = UdpSocket::bind(SocketAddr::V6(SocketAddrV6::new(
284 Ipv6Addr::UNSPECIFIED,
285 0,
286 0,
287 0,
288 )))?;
289 let port = v6.local_addr()?.port();
290
291 let v4 = UdpSocket::bind(SocketAddr::V4(SocketAddrV4::new(
292 Ipv4Addr::UNSPECIFIED,
293 port,
294 )))?;
295
296 assert!(matches!(v6.local_addr()?, SocketAddr::V6(_)));
297 assert!(matches!(v4.local_addr()?, SocketAddr::V4(_)));
298 Ok(())
299 }
300}