noq/
endpoint.rs

1use std::{
2    collections::VecDeque,
3    fmt,
4    future::Future,
5    io::{self, IoSliceMut},
6    mem,
7    net::{SocketAddr, SocketAddrV6},
8    num::NonZeroUsize,
9    pin::Pin,
10    str,
11    sync::{
12        Arc, Mutex,
13        atomic::{AtomicUsize, Ordering},
14    },
15    task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
16};
17
18#[cfg(all(
19    not(wasm_browser),
20    any(feature = "runtime-tokio", feature = "runtime-smol"),
21    any(feature = "aws-lc-rs", feature = "ring"),
22))]
23use crate::runtime::default_runtime;
24use crate::{
25    Instant,
26    runtime::{AsyncUdpSocket, Runtime, UdpSender},
27    udp_transmit,
28};
29use bytes::{Bytes, BytesMut};
30use pin_project_lite::pin_project;
31use proto::{
32    self as proto, ClientConfig, ConnectError, ConnectionError, ConnectionHandle, DatagramEvent,
33    EndpointEvent, FourTuple, NetworkChangeHint, ServerConfig,
34};
35use rustc_hash::FxHashMap;
36#[cfg(all(
37    not(wasm_browser),
38    any(feature = "runtime-tokio", feature = "runtime-smol"),
39    any(feature = "aws-lc-rs", feature = "ring"),
40))]
41use socket2::{Domain, Protocol, Socket, Type};
42use tokio::sync::{Notify, futures::Notified, mpsc};
43use tracing::{Instrument, Span, trace};
44use udp::{BATCH_SIZE, RecvMeta};
45
46use crate::{
47    ConnectionEvent, EndpointConfig, IO_LOOP_BOUND, RECV_TIME_BOUND, VarInt,
48    connection::Connecting, incoming::Incoming, work_limiter::WorkLimiter,
49};
50
51/// A QUIC endpoint.
52///
53/// An endpoint corresponds to a single UDP socket, may host many connections, and may act as both
54/// client and server for different connections.
55///
56/// May be cloned to obtain another handle to the same endpoint.
57#[derive(Debug, Clone)]
58pub struct Endpoint {
59    pub(crate) inner: EndpointRef,
60    runtime: Arc<dyn Runtime>,
61}
62
63impl Endpoint {
64    /// Helper to construct an endpoint for use with outgoing connections only
65    ///
66    /// Note that `addr` is the *local* address to bind to, which should usually be a wildcard
67    /// address like `0.0.0.0:0` or `[::]:0`, which allow communication with any reachable IPv4 or
68    /// IPv6 address respectively from an OS-assigned port.
69    ///
70    /// If an IPv6 address is provided, attempts to make the socket dual-stack so as to allow
71    /// communication with both IPv4 and IPv6 addresses. As such, calling `Endpoint::client` with
72    /// the address `[::]:0` is a reasonable default to maximize the ability to connect to other
73    /// address. For example:
74    ///
75    /// ```
76    /// noq::Endpoint::client((std::net::Ipv6Addr::UNSPECIFIED, 0).into());
77    /// ```
78    ///
79    /// Some environments may not allow creation of dual-stack sockets, in which case an IPv6
80    /// client will only be able to connect to IPv6 servers. An IPv4 client is never dual-stack.
81    #[cfg(all(
82        not(wasm_browser),
83        any(feature = "runtime-tokio", feature = "runtime-smol"),
84        any(feature = "aws-lc-rs", feature = "ring"), // `EndpointConfig::default()` is only available with these
85    ))]
86    pub fn client(addr: SocketAddr) -> io::Result<Self> {
87        let socket = Socket::new(Domain::for_address(addr), Type::DGRAM, Some(Protocol::UDP))?;
88        if addr.is_ipv6()
89            && let Err(e) = socket.set_only_v6(false)
90        {
91            tracing::debug!(%e, "unable to make socket dual-stack");
92        }
93        socket.bind(&addr.into())?;
94        let runtime =
95            default_runtime().ok_or_else(|| io::Error::other("no async runtime found"))?;
96        Self::new_with_abstract_socket(
97            EndpointConfig::default(),
98            None,
99            runtime.wrap_udp_socket(socket.into())?,
100            runtime,
101        )
102    }
103
104    /// Returns relevant stats from this Endpoint
105    pub fn stats(&self) -> EndpointStats {
106        self.inner.state.lock().unwrap().stats
107    }
108
109    /// Helper to construct an endpoint for use with both incoming and outgoing connections
110    ///
111    /// Note that `addr` is the *local* address to bind to, which should usually be a wildcard
112    /// address like `0.0.0.0:0` or `[::]:0`, which allow communication with any reachable IPv4 or
113    /// IPv6 address respectively from an OS-assigned port.
114    ///
115    /// If an IPv6 address is provided, attempts to make the socket dual-stack so as to allow
116    /// communication with both IPv4 and IPv6 clients. As such, calling `Endpoint::server` with
117    /// the address `[::]:0` is a reasonable default to maximize the ability to accept connections
118    /// from any address.
119    ///
120    /// Some environments may not allow creation of dual-stack sockets, in which case an IPv6
121    /// server will only be able to accept connections from IPv6 clients. An IPv4 server is never
122    /// dual-stack.
123    #[cfg(all(
124        not(wasm_browser),
125        any(feature = "runtime-tokio", feature = "runtime-smol"),
126        any(feature = "aws-lc-rs", feature = "ring"), // `EndpointConfig::default()` is only available with these
127    ))]
128    pub fn server(config: ServerConfig, addr: SocketAddr) -> io::Result<Self> {
129        let socket = Socket::new(Domain::for_address(addr), Type::DGRAM, Some(Protocol::UDP))?;
130        if addr.is_ipv6()
131            && let Err(e) = socket.set_only_v6(false)
132        {
133            tracing::debug!(%e, "unable to make socket dual-stack");
134        }
135        socket.bind(&addr.into())?;
136        let runtime =
137            default_runtime().ok_or_else(|| io::Error::other("no async runtime found"))?;
138        Self::new_with_abstract_socket(
139            EndpointConfig::default(),
140            Some(config),
141            runtime.wrap_udp_socket(socket.into())?,
142            runtime,
143        )
144    }
145
146    /// Construct an endpoint with arbitrary configuration and socket
147    #[cfg(not(wasm_browser))]
148    pub fn new(
149        config: EndpointConfig,
150        server_config: Option<ServerConfig>,
151        socket: std::net::UdpSocket,
152        runtime: Arc<dyn Runtime>,
153    ) -> io::Result<Self> {
154        let socket = runtime.wrap_udp_socket(socket)?;
155        Self::new_with_abstract_socket(config, server_config, socket, runtime)
156    }
157
158    /// Construct an endpoint with arbitrary configuration and pre-constructed abstract socket
159    ///
160    /// Useful when `socket` has additional state (e.g. sidechannels) attached for which shared
161    /// ownership is needed.
162    pub fn new_with_abstract_socket(
163        config: EndpointConfig,
164        server_config: Option<ServerConfig>,
165        socket: Box<dyn AsyncUdpSocket>,
166        runtime: Arc<dyn Runtime>,
167    ) -> io::Result<Self> {
168        let addr = socket.local_addr()?;
169        let allow_mtud = !socket.may_fragment();
170        let rc = EndpointRef::new(
171            socket,
172            proto::Endpoint::new(Arc::new(config), server_config.map(Arc::new), allow_mtud),
173            addr.is_ipv6(),
174            runtime.clone(),
175        );
176        let driver = EndpointDriver(rc.clone());
177        runtime.spawn(Box::pin(
178            async {
179                if let Err(e) = driver.await {
180                    tracing::error!("I/O error: {}", e);
181                }
182            }
183            .instrument(Span::current()),
184        ));
185        Ok(Self { inner: rc, runtime })
186    }
187
188    /// Get the next incoming connection attempt from a client
189    ///
190    /// Yields [`Incoming`]s, or `None` if the endpoint is [`close`](Self::close)d. [`Incoming`]
191    /// can be `await`ed to obtain the final [`Connection`](crate::Connection), or used to e.g.
192    /// filter connection attempts or force address validation, or converted into an intermediate
193    /// `Connecting` future which can be used to e.g. send 0.5-RTT data.
194    pub fn accept(&self) -> Accept<'_> {
195        Accept {
196            endpoint: self,
197            notify: self.inner.shared.incoming.notified(),
198        }
199    }
200
201    /// Set the client configuration used by `connect`
202    pub fn set_default_client_config(&self, config: ClientConfig) {
203        self.inner.0.state.lock().unwrap().default_client_config = Some(config);
204    }
205
206    /// Connect to a remote endpoint
207    ///
208    /// `server_name` must be covered by the certificate presented by the server. This prevents a
209    /// connection from being intercepted by an attacker with a valid certificate for some other
210    /// server.
211    ///
212    /// May fail immediately due to configuration errors, or in the future if the connection could
213    /// not be established.
214    pub fn connect(&self, addr: SocketAddr, server_name: &str) -> Result<Connecting, ConnectError> {
215        let Some(config) = self
216            .inner
217            .0
218            .state
219            .lock()
220            .unwrap()
221            .default_client_config
222            .clone()
223        else {
224            return Err(ConnectError::NoDefaultClientConfig);
225        };
226
227        self.connect_with(config, addr, server_name)
228    }
229
230    /// Connect to a remote endpoint using a custom configuration.
231    ///
232    /// See [`connect()`] for details.
233    ///
234    /// [`connect()`]: Endpoint::connect
235    pub fn connect_with(
236        &self,
237        config: ClientConfig,
238        addr: SocketAddr,
239        server_name: &str,
240    ) -> Result<Connecting, ConnectError> {
241        let mut endpoint = self.inner.state.lock().unwrap();
242        if endpoint.driver_lost || endpoint.recv_state.connections.close.is_some() {
243            return Err(ConnectError::EndpointStopping);
244        }
245        if addr.is_ipv6() && !endpoint.ipv6 {
246            return Err(ConnectError::InvalidRemoteAddress(addr));
247        }
248        let addr = if endpoint.ipv6 {
249            SocketAddr::V6(ensure_ipv6(addr))
250        } else {
251            addr
252        };
253
254        let (ch, conn) = endpoint
255            .inner
256            .connect(self.runtime.now(), config, addr, server_name)?;
257
258        let sender = endpoint.socket.create_sender();
259        endpoint.stats.outgoing_handshakes += 1;
260        Ok(endpoint
261            .recv_state
262            .connections
263            .insert(ch, conn, sender, self.runtime.clone()))
264    }
265
266    /// Switch to a new UDP socket
267    ///
268    /// See [`Endpoint::rebind_abstract()`] for details.
269    #[cfg(not(wasm_browser))]
270    pub fn rebind(&self, socket: std::net::UdpSocket) -> io::Result<()> {
271        self.rebind_abstract(self.runtime.wrap_udp_socket(socket)?)
272    }
273
274    /// Switch to a new UDP socket
275    ///
276    /// Allows the endpoint's address to be updated live, affecting all active connections. Incoming
277    /// connections and connections to servers unreachable from the new address will be lost.
278    ///
279    /// On error, the old UDP socket is retained.
280    pub fn rebind_abstract(&self, socket: Box<dyn AsyncUdpSocket>) -> io::Result<()> {
281        let addr = socket.local_addr()?;
282        let mut inner = self.inner.state.lock().unwrap();
283        inner.prev_socket = Some(mem::replace(&mut inner.socket, socket));
284        inner.ipv6 = addr.is_ipv6();
285
286        // Update connection socket references
287        for sender in inner.recv_state.connections.senders.values() {
288            // Ignoring errors from dropped connections
289            let _ = sender.send(ConnectionEvent::Rebind(inner.socket.create_sender()));
290        }
291        if let Some(driver) = inner.driver.take() {
292            // Ensure the driver can register for wake-ups from the new socket
293            driver.wake();
294        }
295
296        Ok(())
297    }
298
299    /// Notify connections that the local network address has changed.
300    ///
301    /// This informs all active connections that the local address may have changed (e.g., due to a
302    /// network interface change), triggering liveness checks and recovery procedures on each
303    /// connection without requiring a socket rebind.
304    ///
305    /// Unlike [`Self::rebind`], this does not change the underlying socket. Use this when the
306    /// network topology changes but the socket remains valid (e.g., if bound to the unspecified
307    /// address and switching from WiFi to cellular, or when the local IP address changes).
308    ///
309    /// The optional `hint` allows callers to indicate which paths may still be recoverable after
310    /// the network change. If `None`, all paths are assumed to be non-recoverable. For client-side
311    /// multipath connections, unrecoverable paths will be closed and replaced with new paths to
312    /// the same remote addresses.
313    pub fn handle_network_change(&self, hint: Option<Arc<dyn NetworkChangeHint + Sync + Send>>) {
314        let mut inner = self.inner.state.lock().unwrap();
315        for sender in inner.recv_state.connections.senders.values() {
316            // Ignoring errors from dropped connections
317            let _ = sender.send(ConnectionEvent::LocalAddressChanged(hint.clone()));
318        }
319        if let Some(driver) = inner.driver.take() {
320            driver.wake();
321        }
322    }
323
324    /// Replace the server configuration, affecting new incoming connections only
325    ///
326    /// Useful for e.g. refreshing TLS certificates without disrupting existing connections.
327    pub fn set_server_config(&self, server_config: Option<ServerConfig>) {
328        self.inner
329            .state
330            .lock()
331            .unwrap()
332            .inner
333            .set_server_config(server_config.map(Arc::new))
334    }
335
336    /// Get the local `SocketAddr` the underlying socket is bound to
337    pub fn local_addr(&self) -> io::Result<SocketAddr> {
338        self.inner.state.lock().unwrap().socket.local_addr()
339    }
340
341    /// Get the number of connections that are currently open
342    pub fn open_connections(&self) -> usize {
343        self.inner.state.lock().unwrap().inner.open_connections()
344    }
345
346    /// Close all of this endpoint's connections immediately and cease accepting new connections.
347    ///
348    /// See [`Connection::close()`] for details.
349    ///
350    /// [`Connection::close()`]: crate::Connection::close
351    pub fn close(&self, error_code: VarInt, reason: &[u8]) {
352        let reason = Bytes::copy_from_slice(reason);
353        let mut endpoint = self.inner.state.lock().unwrap();
354        endpoint.recv_state.connections.close = Some((error_code, reason.clone()));
355        for sender in endpoint.recv_state.connections.senders.values() {
356            // Ignoring errors from dropped connections
357            let _ = sender.send(ConnectionEvent::Close {
358                error_code,
359                reason: reason.clone(),
360            });
361        }
362        self.inner.shared.incoming.notify_waiters();
363        // Wake the endpoint driver so that it can shutdown if the endpoint is already drained.
364        if let Some(waker) = endpoint.driver.take() {
365            waker.wake();
366        }
367    }
368
369    /// Waits for all connections on the endpoint to be cleanly shut down and drained.
370    ///
371    /// This is equivalent to [`wait_all_draining()`] with additionally waiting for the connections to be
372    /// drained. Please see its documentation for more information.
373    ///
374    /// Use `wait_idle()` in favor of `wait_all_draining()` if you care about waiting for the
375    /// [`Connection`] structs to be dropped.
376    ///
377    /// [`wait_all_draining()`]: Self::wait_all_draining
378    /// [`Connection`]: crate::Connection
379    pub async fn wait_idle(&self) {
380        loop {
381            {
382                let endpoint = &mut *self.inner.state.lock().unwrap();
383                if endpoint.recv_state.connections.is_empty() {
384                    break;
385                }
386                // Construct future while lock is held to avoid race
387                self.inner.shared.idle.notified()
388            }
389            .await;
390        }
391    }
392
393    /// Waits for all connections on the endpoint to be ready for shutting down.
394    ///
395    /// Waiting for this condition before exiting ensures that a good-faith effort is made to notify
396    /// peers of recent connection closes, whereas exiting immediately could force them to wait out
397    /// the idle timeout period.
398    ///
399    /// Does not proactively close existing connections or cause incoming connections to be
400    /// rejected. Consider calling [`close()`] if that is desired.
401    ///
402    /// Unlike [`wait_idle()`], this doesn't wait for the full draining period, so it can't be
403    /// used to wait for all now-idle [`Connection`]s to be dropped.
404    ///
405    /// See also this section in the QUIC RFC: <https://datatracker.ietf.org/doc/html/rfc9000#section-10.2-6>
406    ///
407    /// [`close()`]: Self::close
408    /// [`wait_idle()`]: Self::wait_idle
409    /// [`Connection`]: crate::Connection
410    pub async fn wait_all_draining(&self) {
411        loop {
412            {
413                let endpoint = &mut *self.inner.state.lock().unwrap();
414                if endpoint.recv_state.connections.active_connections == 0 {
415                    break;
416                }
417                // Construct future while lock is held to avoid race
418                self.inner.shared.all_draining.notified()
419            }
420            .await;
421        }
422    }
423}
424
425/// Statistics on [Endpoint] activity
426#[non_exhaustive]
427#[derive(Debug, Default, Copy, Clone)]
428pub struct EndpointStats {
429    /// Cumulative number of Quic handshakes accepted by this [Endpoint]
430    pub accepted_handshakes: u64,
431    /// Cumulative number of Quic handshakes sent from this [Endpoint]
432    pub outgoing_handshakes: u64,
433    /// Cumulative number of Quic handshakes refused on this [Endpoint]
434    pub refused_handshakes: u64,
435    /// Cumulative number of Quic handshakes ignored on this [Endpoint]
436    pub ignored_handshakes: u64,
437}
438
439/// A future that drives IO on an endpoint
440///
441/// This task functions as the switch point between the UDP socket object and the
442/// `Endpoint` responsible for routing datagrams to their owning `Connection`.
443/// In order to do so, it also facilitates the exchange of different types of events
444/// flowing between the `Endpoint` and the tasks managing `Connection`s. As such,
445/// running this task is necessary to keep the endpoint's connections running.
446///
447/// `EndpointDriver` futures terminate when all clones of the `Endpoint` have been dropped, or when
448/// an I/O error occurs.
449#[must_use = "endpoint drivers must be spawned for I/O to occur"]
450#[derive(Debug)]
451pub(crate) struct EndpointDriver(pub(crate) EndpointRef);
452
453impl Future for EndpointDriver {
454    type Output = Result<(), io::Error>;
455
456    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
457        let mut endpoint = self.0.state.lock().unwrap();
458        if endpoint.driver.is_none() {
459            endpoint.driver = Some(cx.waker().clone());
460        }
461
462        let now = endpoint.runtime.now();
463        let mut keep_going = false;
464        keep_going |= endpoint.drive_recv(cx, now)?;
465        keep_going |= endpoint.handle_events(cx, &self.0.shared);
466
467        if !endpoint.recv_state.incoming.is_empty() {
468            self.0.shared.incoming.notify_waiters();
469        }
470
471        // Stop the driver if either:
472        // - all `Endpoint` structs are dropped and all connections are drained,
473        // - or `Endpoint::close` has been called and all connections are drained.
474        if endpoint.recv_state.connections.is_empty()
475            && (self.0.shared.ref_count.load(Ordering::Relaxed) == 0
476                || endpoint.recv_state.connections.close.is_some())
477        {
478            trace!("endpoint driver stopping");
479            Poll::Ready(Ok(()))
480        } else {
481            drop(endpoint);
482            // If there is more work to do schedule the endpoint task again.
483            // `wake_by_ref()` is called outside the lock to minimize
484            // lock contention on a multithreaded runtime.
485            if keep_going {
486                cx.waker().wake_by_ref();
487            }
488            Poll::Pending
489        }
490    }
491}
492
493impl Drop for EndpointDriver {
494    fn drop(&mut self) {
495        let mut endpoint = self.0.state.lock().unwrap();
496        endpoint.driver_lost = true;
497        self.0.shared.incoming.notify_waiters();
498        // Drop all outgoing channels, signaling the termination of the endpoint to the associated
499        // connections.
500        endpoint.recv_state.connections.senders.clear();
501        endpoint.recv_state.connections.active_connections = 0;
502    }
503}
504
505#[derive(Debug)]
506pub(crate) struct EndpointInner {
507    pub(crate) state: Mutex<State>,
508    pub(crate) shared: Shared,
509}
510
511impl EndpointInner {
512    pub(crate) fn accept(
513        &self,
514        incoming: proto::Incoming,
515        server_config: Option<Arc<ServerConfig>>,
516    ) -> Result<Connecting, ConnectionError> {
517        let mut state = self.state.lock().unwrap();
518        let mut response_buffer = Vec::new();
519        let now = state.runtime.now();
520        match state
521            .inner
522            .accept(incoming, now, &mut response_buffer, server_config)
523        {
524            Ok((handle, conn)) => {
525                state.stats.accepted_handshakes += 1;
526                let sender = state.socket.create_sender();
527                let runtime = state.runtime.clone();
528                Ok(state
529                    .recv_state
530                    .connections
531                    .insert(handle, conn, sender, runtime))
532            }
533            Err(error) => {
534                if let Some(transmit) = error.response {
535                    respond(transmit, &response_buffer, &mut state.sender);
536                }
537                Err(error.cause)
538            }
539        }
540    }
541
542    pub(crate) fn refuse(&self, incoming: proto::Incoming) {
543        let mut state = self.state.lock().unwrap();
544        state.stats.refused_handshakes += 1;
545        let mut response_buffer = Vec::new();
546        let transmit = state.inner.refuse(incoming, &mut response_buffer);
547        respond(transmit, &response_buffer, &mut state.sender);
548    }
549
550    pub(crate) fn retry(&self, incoming: proto::Incoming) -> Result<(), proto::RetryError> {
551        let mut state = self.state.lock().unwrap();
552        let mut response_buffer = Vec::new();
553        let transmit = state.inner.retry(incoming, &mut response_buffer)?;
554        respond(transmit, &response_buffer, &mut state.sender);
555        Ok(())
556    }
557
558    pub(crate) fn ignore(&self, incoming: proto::Incoming) {
559        let mut state = self.state.lock().unwrap();
560        state.stats.ignored_handshakes += 1;
561        state.inner.ignore(incoming);
562    }
563}
564
565#[derive(Debug)]
566pub(crate) struct State {
567    socket: Box<dyn AsyncUdpSocket>,
568    sender: Pin<Box<dyn UdpSender>>,
569    /// During an active migration, abandoned_socket receives traffic
570    /// until the first packet arrives on the new socket.
571    prev_socket: Option<Box<dyn AsyncUdpSocket>>,
572    inner: proto::Endpoint,
573    recv_state: RecvState,
574    driver: Option<Waker>,
575    ipv6: bool,
576    events: mpsc::UnboundedReceiver<(ConnectionHandle, EndpointEvent)>,
577    driver_lost: bool,
578    runtime: Arc<dyn Runtime>,
579    stats: EndpointStats,
580    default_client_config: Option<ClientConfig>,
581}
582
583#[derive(Debug)]
584pub(crate) struct Shared {
585    /// Notifies subscribers of new incoming connections.
586    ///
587    /// This enables the `Endpoint::accept` API.
588    incoming: Notify,
589    /// Notifies subscribers when *all* connections have entered the draining state.
590    ///
591    /// This powers the `Endpoint::wait_idle` API.
592    all_draining: Notify,
593    /// Notifies subscribesr when *all* connections have been dropped.
594    ///
595    /// This powers the `Endpoint::wait_drained` API.
596    idle: Notify,
597    /// Number of live handles that can be used to initiate or handle I/O; excludes the driver
598    ref_count: AtomicUsize,
599}
600
601impl State {
602    fn drive_recv(&mut self, cx: &mut Context<'_>, now: Instant) -> Result<bool, io::Error> {
603        let get_time = || self.runtime.now();
604        self.recv_state.recv_limiter.start_cycle(get_time);
605        if let Some(socket) = &mut self.prev_socket {
606            // We don't care about the `PollProgress` from old sockets.
607            let poll_res = self.recv_state.poll_socket(
608                cx,
609                &mut self.inner,
610                &mut **socket,
611                &mut self.sender,
612                &*self.runtime,
613                now,
614            );
615            if poll_res.is_err() {
616                self.prev_socket = None;
617            }
618        };
619        let poll_res = self.recv_state.poll_socket(
620            cx,
621            &mut self.inner,
622            &mut *self.socket,
623            &mut self.sender,
624            &*self.runtime,
625            now,
626        );
627        self.recv_state.recv_limiter.finish_cycle(get_time);
628        let poll_res = poll_res?;
629        if poll_res.received_connection_packet {
630            // Traffic has arrived on self.socket, therefore there is no need for the abandoned
631            // one anymore. TODO: Account for multiple outgoing connections.
632            self.prev_socket = None;
633        }
634        Ok(poll_res.keep_going)
635    }
636
637    fn handle_events(&mut self, cx: &mut Context<'_>, shared: &Shared) -> bool {
638        for _ in 0..IO_LOOP_BOUND {
639            let (ch, event) = match self.events.poll_recv(cx) {
640                Poll::Ready(Some(x)) => x,
641                Poll::Ready(None) => unreachable!("EndpointInner owns one sender"),
642                Poll::Pending => {
643                    return false;
644                }
645            };
646
647            if event.is_draining() {
648                self.recv_state.connections.active_connections -= 1;
649                if self.recv_state.connections.active_connections == 0 {
650                    shared.all_draining.notify_waiters();
651                }
652            } else if event.is_drained() {
653                self.recv_state.connections.senders.remove(&ch);
654                if self.recv_state.connections.is_empty() {
655                    shared.idle.notify_waiters();
656                }
657            }
658            let Some(event) = self.inner.handle_event(ch, event) else {
659                continue;
660            };
661            // Ignoring errors from dropped connections that haven't yet been cleaned up
662            let _ = self
663                .recv_state
664                .connections
665                .senders
666                .get_mut(&ch)
667                .unwrap()
668                .send(ConnectionEvent::Proto(event));
669        }
670
671        true
672    }
673}
674
675impl Drop for State {
676    fn drop(&mut self) {
677        for incoming in self.recv_state.incoming.drain(..) {
678            self.inner.ignore(incoming);
679        }
680    }
681}
682
683fn respond(
684    transmit: proto::Transmit,
685    response_buffer: &[u8],
686    sender: &mut Pin<Box<dyn UdpSender>>,
687) {
688    // Send if there's kernel buffer space; otherwise, drop it
689    //
690    // As an endpoint-generated packet, we know this is an
691    // immediate, stateless response to an unconnected peer,
692    // one of:
693    //
694    // - A version negotiation response due to an unknown version
695    // - A `CLOSE` due to a malformed or unwanted connection attempt
696    // - A stateless reset due to an unrecognized connection
697    // - A `Retry` packet due to a connection attempt when
698    //   `use_retry` is set
699    //
700    // In each case, a well-behaved peer can be trusted to retry a
701    // few times, which is guaranteed to produce the same response
702    // from us. Repeated failures might at worst cause a peer's new
703    // connection attempt to time out, which is acceptable if we're
704    // under such heavy load that there's never room for this code
705    // to transmit. This is morally equivalent to the packet getting
706    // lost due to congestion further along the link, which
707    // similarly relies on peer retries for recovery.
708
709    // Copied from rust 1.85's std::task::Waker::noop() implementation for backwards compatibility
710    const NOOP: RawWaker = {
711        const VTABLE: RawWakerVTable = RawWakerVTable::new(
712            // Cloning just returns a new no-op raw waker
713            |_| NOOP,
714            // `wake` does nothing
715            |_| {},
716            // `wake_by_ref` does nothing
717            |_| {},
718            // Dropping does nothing as we don't allocate anything
719            |_| {},
720        );
721        RawWaker::new(std::ptr::null(), &VTABLE)
722    };
723    // SAFETY: Copied from rust stdlib, the NOOP waker is thread-safe and doesn't violate the RawWakerVTable contract,
724    // it doesn't access the data pointer at all.
725    let waker = unsafe { Waker::from_raw(NOOP) };
726    let mut cx = Context::from_waker(&waker);
727    _ = sender.as_mut().poll_send(
728        &udp_transmit(&transmit, &response_buffer[..transmit.size]),
729        &mut cx,
730    );
731}
732
733#[inline]
734fn proto_ecn(ecn: udp::EcnCodepoint) -> proto::EcnCodepoint {
735    match ecn {
736        udp::EcnCodepoint::Ect0 => proto::EcnCodepoint::Ect0,
737        udp::EcnCodepoint::Ect1 => proto::EcnCodepoint::Ect1,
738        udp::EcnCodepoint::Ce => proto::EcnCodepoint::Ce,
739    }
740}
741
742#[derive(Debug)]
743struct ConnectionSet {
744    /// Senders for communicating with the endpoint's connections
745    senders: FxHashMap<ConnectionHandle, mpsc::UnboundedSender<ConnectionEvent>>,
746    /// Stored to give out clones to new ConnectionInners
747    sender: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
748    /// Set if the endpoint has been manually closed
749    close: Option<(VarInt, Bytes)>,
750    /// Counter for all active (non-draining/drained) connections.
751    ///
752    /// This is directly related to the QUIC connection states "Initial", "Handshake",
753    /// "Established", "Closed", "Draining" and "Drained" (see also `proto/src/connection/state.rs`).
754    ///
755    /// Any connection state that is not "Draining" or "Drained" is considered active.
756    ///
757    /// This counter is updated when new connections are added ([`ConnectionSet::insert`]) and when
758    /// a connection informs us about entering the draining state ([`State::handle_events`]).
759    active_connections: u64,
760}
761
762impl ConnectionSet {
763    fn insert(
764        &mut self,
765        handle: ConnectionHandle,
766        conn: proto::Connection,
767        sender: Pin<Box<dyn UdpSender>>,
768        runtime: Arc<dyn Runtime>,
769    ) -> Connecting {
770        let (send, recv) = mpsc::unbounded_channel();
771        if let Some((error_code, ref reason)) = self.close {
772            send.send(ConnectionEvent::Close {
773                error_code,
774                reason: reason.clone(),
775            })
776            .unwrap();
777        }
778        self.senders.insert(handle, send);
779        self.active_connections += 1;
780        Connecting::new(handle, conn, self.sender.clone(), recv, sender, runtime)
781    }
782
783    fn is_empty(&self) -> bool {
784        self.senders.is_empty()
785    }
786}
787
788pub(crate) fn ensure_ipv6(x: SocketAddr) -> SocketAddrV6 {
789    match x {
790        SocketAddr::V6(x) => x,
791        SocketAddr::V4(x) => SocketAddrV6::new(x.ip().to_ipv6_mapped(), x.port(), 0, 0),
792    }
793}
794
795pin_project! {
796    /// Future produced by [`Endpoint::accept`]
797    pub struct Accept<'a> {
798        endpoint: &'a Endpoint,
799        #[pin]
800        notify: Notified<'a>,
801    }
802}
803
804impl Future for Accept<'_> {
805    type Output = Option<Incoming>;
806    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
807        let mut this = self.project();
808        let mut endpoint = this.endpoint.inner.state.lock().unwrap();
809        if endpoint.driver_lost {
810            return Poll::Ready(None);
811        }
812        if let Some(incoming) = endpoint.recv_state.incoming.pop_front() {
813            // Release the mutex lock on endpoint so cloning it doesn't deadlock
814            drop(endpoint);
815            let incoming = Incoming::new(incoming, this.endpoint.inner.clone());
816            return Poll::Ready(Some(incoming));
817        }
818        if endpoint.recv_state.connections.close.is_some() {
819            return Poll::Ready(None);
820        }
821        loop {
822            match this.notify.as_mut().poll(ctx) {
823                // `state` lock ensures we didn't race with readiness
824                Poll::Pending => return Poll::Pending,
825                // Spurious wakeup, get a new future
826                Poll::Ready(()) => this
827                    .notify
828                    .set(this.endpoint.inner.shared.incoming.notified()),
829            }
830        }
831    }
832}
833
834#[derive(Debug)]
835pub(crate) struct EndpointRef(Arc<EndpointInner>);
836
837impl EndpointRef {
838    pub(crate) fn new(
839        socket: Box<dyn AsyncUdpSocket>,
840        inner: proto::Endpoint,
841        ipv6: bool,
842        runtime: Arc<dyn Runtime>,
843    ) -> Self {
844        let (sender, events) = mpsc::unbounded_channel();
845        let recv_state = RecvState::new(sender, socket.max_receive_segments(), &inner);
846        let sender = socket.create_sender();
847        Self(Arc::new(EndpointInner {
848            shared: Shared {
849                incoming: Notify::new(),
850                all_draining: Notify::new(),
851                idle: Notify::new(),
852                ref_count: AtomicUsize::new(0),
853            },
854            state: Mutex::new(State {
855                socket,
856                sender,
857                prev_socket: None,
858                inner,
859                ipv6,
860                events,
861                driver: None,
862                driver_lost: false,
863                recv_state,
864                runtime,
865                stats: EndpointStats::default(),
866                default_client_config: None,
867            }),
868        }))
869    }
870}
871
872impl Clone for EndpointRef {
873    fn clone(&self) -> Self {
874        self.0.shared.ref_count.fetch_add(1, Ordering::Relaxed);
875        Self(self.0.clone())
876    }
877}
878
879impl Drop for EndpointRef {
880    fn drop(&mut self) {
881        if self.0.shared.ref_count.fetch_sub(1, Ordering::Relaxed) > 1 {
882            return;
883        }
884
885        let endpoint = &mut *self.0.state.lock().unwrap();
886        // If the driver is about to be on its own, ensure it can shut down if the last
887        // connection is gone.
888        if let Some(task) = endpoint.driver.take() {
889            task.wake();
890        }
891    }
892}
893
894impl std::ops::Deref for EndpointRef {
895    type Target = EndpointInner;
896    fn deref(&self) -> &Self::Target {
897        &self.0
898    }
899}
900
901/// State directly involved in handling incoming packets
902struct RecvState {
903    incoming: VecDeque<proto::Incoming>,
904    connections: ConnectionSet,
905    recv_buf: Box<[u8]>,
906    recv_limiter: WorkLimiter,
907}
908
909impl RecvState {
910    fn new(
911        sender: mpsc::UnboundedSender<(ConnectionHandle, EndpointEvent)>,
912        max_receive_segments: NonZeroUsize,
913        endpoint: &proto::Endpoint,
914    ) -> Self {
915        let recv_buf = vec![
916            0;
917            endpoint.config().get_max_udp_payload_size().min(64 * 1024) as usize
918                * max_receive_segments.get()
919                * BATCH_SIZE
920        ];
921        Self {
922            connections: ConnectionSet {
923                senders: FxHashMap::default(),
924                sender,
925                close: None,
926                active_connections: 0,
927            },
928            incoming: VecDeque::new(),
929            recv_buf: recv_buf.into(),
930            recv_limiter: WorkLimiter::new(RECV_TIME_BOUND),
931        }
932    }
933
934    fn poll_socket(
935        &mut self,
936        cx: &mut Context<'_>,
937        endpoint: &mut proto::Endpoint,
938        socket: &mut dyn AsyncUdpSocket,
939        sender: &mut Pin<Box<dyn UdpSender>>,
940        runtime: &dyn Runtime,
941        now: Instant,
942    ) -> Result<PollProgress, io::Error> {
943        let mut received_connection_packet = false;
944        let mut metas = [RecvMeta::default(); BATCH_SIZE];
945        let mut iovs: [IoSliceMut<'_>; BATCH_SIZE] = {
946            let mut bufs = self
947                .recv_buf
948                .chunks_mut(self.recv_buf.len() / BATCH_SIZE)
949                .map(IoSliceMut::new);
950
951            // expect() safe as self.recv_buf is chunked into BATCH_SIZE items
952            // and iovs will be of size BATCH_SIZE, thus from_fn is called
953            // exactly BATCH_SIZE times.
954            std::array::from_fn(|_| bufs.next().expect("BATCH_SIZE elements"))
955        };
956        loop {
957            match socket.poll_recv(cx, &mut iovs, &mut metas) {
958                Poll::Ready(Ok(msgs)) => {
959                    self.recv_limiter.record_work(msgs);
960                    for (meta, buf) in metas.iter().zip(iovs.iter()).take(msgs) {
961                        let mut data: BytesMut = buf[0..meta.len].into();
962                        while !data.is_empty() {
963                            let buf = data.split_to(meta.stride.min(data.len()));
964                            let mut response_buffer = Vec::new();
965                            let addresses = FourTuple::new(meta.addr, meta.dst_ip);
966                            match endpoint.handle(
967                                now,
968                                addresses,
969                                meta.ecn.map(proto_ecn),
970                                buf,
971                                &mut response_buffer,
972                            ) {
973                                Some(DatagramEvent::NewConnection(incoming)) => {
974                                    if self.connections.close.is_none() {
975                                        self.incoming.push_back(incoming);
976                                    } else {
977                                        let transmit =
978                                            endpoint.refuse(incoming, &mut response_buffer);
979                                        respond(transmit, &response_buffer, sender);
980                                    }
981                                }
982                                Some(DatagramEvent::ConnectionEvent(handle, event)) => {
983                                    // Ignoring errors from dropped connections that haven't yet been cleaned up
984                                    received_connection_packet = true;
985                                    let _ = self
986                                        .connections
987                                        .senders
988                                        .get_mut(&handle)
989                                        .unwrap()
990                                        .send(ConnectionEvent::Proto(event));
991                                }
992                                Some(DatagramEvent::Response(transmit)) => {
993                                    respond(transmit, &response_buffer, sender);
994                                }
995                                None => {}
996                            }
997                        }
998                    }
999                }
1000                Poll::Pending => {
1001                    return Ok(PollProgress {
1002                        received_connection_packet,
1003                        keep_going: false,
1004                    });
1005                }
1006                // Ignore ECONNRESET as it's undefined in QUIC and may be injected by an
1007                // attacker
1008                Poll::Ready(Err(ref e)) if e.kind() == io::ErrorKind::ConnectionReset => {
1009                    continue;
1010                }
1011                Poll::Ready(Err(e)) => {
1012                    return Err(e);
1013                }
1014            }
1015            if !self.recv_limiter.allow_work(|| runtime.now()) {
1016                return Ok(PollProgress {
1017                    received_connection_packet,
1018                    keep_going: true,
1019                });
1020            }
1021        }
1022    }
1023}
1024
1025impl fmt::Debug for RecvState {
1026    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1027        f.debug_struct("RecvState")
1028            .field("incoming", &self.incoming)
1029            .field("connections", &self.connections)
1030            // recv_buf too large
1031            .field("recv_limiter", &self.recv_limiter)
1032            .finish_non_exhaustive()
1033    }
1034}
1035
1036#[derive(Default)]
1037struct PollProgress {
1038    /// Whether a datagram was routed to an existing connection
1039    received_connection_packet: bool,
1040    /// Whether datagram handling was interrupted early by the work limiter for fairness
1041    keep_going: bool,
1042}