Skip to main content

quinn/
endpoint.rs

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