noq_proto/
endpoint.rs

1use std::{
2    collections::{HashMap, hash_map},
3    convert::TryFrom,
4    fmt, mem,
5    net::{IpAddr, SocketAddr},
6    ops::{Index, IndexMut},
7    sync::Arc,
8};
9
10use bytes::{Buf, BufMut, Bytes, BytesMut};
11use rand::{Rng, RngExt, SeedableRng, rngs::StdRng};
12use rustc_hash::FxHashMap;
13use slab::Slab;
14use thiserror::Error;
15use tracing::{debug, error, trace, warn};
16
17use crate::{
18    Duration, FourTuple, INITIAL_MTU, Instant, MAX_CID_SIZE, MIN_INITIAL_SIZE, PathId,
19    RESET_TOKEN_SIZE, ResetToken, Side, Transmit, TransportConfig, TransportError,
20    cid_generator::ConnectionIdGenerator,
21    coding::{BufMutExt, Decodable, Encodable, UnexpectedEnd},
22    config::{ClientConfig, EndpointConfig, ServerConfig},
23    connection::{Connection, ConnectionError, SideArgs},
24    crypto::{self, Keys, UnsupportedVersion},
25    frame,
26    packet::{
27        FixedLengthConnectionIdParser, Header, InitialHeader, InitialPacket, PacketDecodeError,
28        PacketNumber, PartialDecode, ProtectedInitialHeader,
29    },
30    shared::{
31        ConnectionEvent, ConnectionEventInner, ConnectionId, DatagramConnectionEvent, EcnCodepoint,
32        EndpointEvent, EndpointEventInner, IssuedCid,
33    },
34    token::{IncomingToken, InvalidRetryTokenError, Token, TokenPayload},
35    transport_parameters::{PreferredAddress, TransportParameters},
36};
37
38/// The main entry point to the library
39///
40/// This object performs no I/O whatsoever. Instead, it consumes incoming packets and
41/// connection-generated events via `handle` and `handle_event`.
42pub struct Endpoint {
43    rng: StdRng,
44    index: ConnectionIndex,
45    connections: Slab<ConnectionMeta>,
46    local_cid_generator: Box<dyn ConnectionIdGenerator>,
47    config: Arc<EndpointConfig>,
48    server_config: Option<Arc<ServerConfig>>,
49    /// Whether the underlying UDP socket promises not to fragment packets
50    allow_mtud: bool,
51    /// Time at which a stateless reset was most recently sent
52    last_stateless_reset: Option<Instant>,
53    /// Buffered Initial and 0-RTT messages for pending incoming connections
54    incoming_buffers: Slab<IncomingBuffer>,
55    all_incoming_buffers_total_bytes: u64,
56}
57
58impl Endpoint {
59    /// Create a new endpoint
60    ///
61    /// `allow_mtud` enables path MTU detection when requested by `Connection` configuration for
62    /// better performance. This requires that outgoing packets are never fragmented, which can be
63    /// achieved via e.g. the `IPV6_DONTFRAG` socket option.
64    pub fn new(
65        config: Arc<EndpointConfig>,
66        server_config: Option<Arc<ServerConfig>>,
67        allow_mtud: bool,
68    ) -> Self {
69        Self {
70            rng: config
71                .rng_seed
72                .map_or_else(|| StdRng::from_rng(&mut rand::rng()), StdRng::from_seed),
73            index: ConnectionIndex::default(),
74            connections: Slab::new(),
75            local_cid_generator: (config.connection_id_generator_factory.as_ref())(),
76            config,
77            server_config,
78            allow_mtud,
79            last_stateless_reset: None,
80            incoming_buffers: Slab::new(),
81            all_incoming_buffers_total_bytes: 0,
82        }
83    }
84
85    /// Replace the server configuration, affecting new incoming connections only
86    pub fn set_server_config(&mut self, server_config: Option<Arc<ServerConfig>>) {
87        self.server_config = server_config;
88    }
89
90    /// Process `EndpointEvent`s emitted from related `Connection`s
91    ///
92    /// In turn, processing this event may return a `ConnectionEvent` for the same `Connection`.
93    pub fn handle_event(
94        &mut self,
95        ch: ConnectionHandle,
96        event: EndpointEvent,
97    ) -> Option<ConnectionEvent> {
98        use EndpointEventInner::*;
99        match event.0 {
100            NeedIdentifiers(path_id, now, n) => {
101                return Some(self.send_new_identifiers(path_id, now, ch, n));
102            }
103            ResetToken(path_id, remote, token) => {
104                if let Some(old) = self.connections[ch]
105                    .reset_token
106                    .insert(path_id, (remote, token))
107                {
108                    self.index.connection_reset_tokens.remove(old.0, old.1);
109                }
110                if self.index.connection_reset_tokens.insert(remote, token, ch) {
111                    warn!("duplicate reset token");
112                }
113            }
114            RetireResetToken(path_id) => {
115                if let Some(old) = self.connections[ch].reset_token.remove(&path_id) {
116                    self.index.connection_reset_tokens.remove(old.0, old.1);
117                }
118            }
119            RetireConnectionId(now, path_id, seq, allow_more_cids) => {
120                if let Some(cid) = self.connections[ch]
121                    .local_cids
122                    .get_mut(&path_id)
123                    .and_then(|pcid| pcid.cids.remove(&seq))
124                {
125                    trace!(%path_id, "local CID retired {}: {}", seq, cid);
126                    self.index.retire(cid);
127                    if allow_more_cids {
128                        return Some(self.send_new_identifiers(path_id, now, ch, 1));
129                    }
130                }
131            }
132            Draining => {
133                // Nothing to do.
134            }
135            Drained => {
136                if let Some(conn) = self.connections.try_remove(ch.0) {
137                    self.index.remove(&conn);
138                } else {
139                    // This indicates a bug in downstream code, which could cause spurious
140                    // connection loss instead of this error if the CID was (re)allocated prior to
141                    // the illegal call.
142                    error!(id = ch.0, "unknown connection drained");
143                }
144            }
145        }
146        None
147    }
148
149    /// Process an incoming UDP datagram
150    pub fn handle(
151        &mut self,
152        now: Instant,
153        network_path: FourTuple,
154        ecn: Option<EcnCodepoint>,
155        data: BytesMut,
156        buf: &mut Vec<u8>,
157    ) -> Option<DatagramEvent> {
158        // Partially decode packet or short-circuit if unable
159        let datagram_len = data.len();
160        let mut event = match PartialDecode::new(
161            data,
162            &FixedLengthConnectionIdParser::new(self.local_cid_generator.cid_len()),
163            &self.config.supported_versions,
164            self.config.grease_quic_bit,
165        ) {
166            Ok((first_decode, remaining)) => DatagramConnectionEvent {
167                now,
168                network_path,
169                path_id: PathId::ZERO, // Corrected later for existing paths
170                ecn,
171                first_decode,
172                remaining,
173            },
174            Err(PacketDecodeError::UnsupportedVersion {
175                src_cid,
176                dst_cid,
177                version,
178            }) => {
179                if self.server_config.is_none() {
180                    debug!("dropping packet with unsupported version");
181                    return None;
182                }
183                trace!("sending version negotiation");
184                // Negotiate versions
185                Header::VersionNegotiate {
186                    random: self.rng.random::<u8>() | 0x40,
187                    src_cid: dst_cid,
188                    dst_cid: src_cid,
189                }
190                .encode(buf);
191                // Grease with a reserved version
192                buf.write::<u32>(match version {
193                    0x0a1a_2a3a => 0x0a1a_2a4a,
194                    _ => 0x0a1a_2a3a,
195                });
196                for &version in &self.config.supported_versions {
197                    buf.write(version);
198                }
199                return Some(DatagramEvent::Response(Transmit {
200                    destination: network_path.remote,
201                    ecn: None,
202                    size: buf.len(),
203                    segment_size: None,
204                    src_ip: network_path.local_ip,
205                }));
206            }
207            Err(e) => {
208                trace!("malformed header: {}", e);
209                return None;
210            }
211        };
212
213        let dst_cid = event.first_decode.dst_cid();
214
215        if let Some(route_to) = self.index.get(&network_path, &event.first_decode) {
216            event.path_id = match route_to {
217                RouteDatagramTo::Incoming(_) => PathId::ZERO,
218                RouteDatagramTo::Connection(_, path_id) => path_id,
219            };
220            match route_to {
221                RouteDatagramTo::Incoming(incoming_idx) => {
222                    let incoming_buffer = &mut self.incoming_buffers[incoming_idx];
223                    let config = &self.server_config.as_ref().unwrap();
224
225                    if incoming_buffer
226                        .total_bytes
227                        .checked_add(datagram_len as u64)
228                        .is_some_and(|n| n <= config.incoming_buffer_size)
229                        && self
230                            .all_incoming_buffers_total_bytes
231                            .checked_add(datagram_len as u64)
232                            .is_some_and(|n| n <= config.incoming_buffer_size_total)
233                    {
234                        incoming_buffer.datagrams.push(event);
235                        incoming_buffer.total_bytes += datagram_len as u64;
236                        self.all_incoming_buffers_total_bytes += datagram_len as u64;
237                    }
238
239                    None
240                }
241                RouteDatagramTo::Connection(ch, _path_id) => Some(DatagramEvent::ConnectionEvent(
242                    ch,
243                    ConnectionEvent(ConnectionEventInner::Datagram(event)),
244                )),
245            }
246        } else if event.first_decode.initial_header().is_some() {
247            // Potentially create a new connection
248
249            self.handle_first_packet(datagram_len, event, network_path, buf)
250        } else if event.first_decode.has_long_header() {
251            debug!(
252                "ignoring non-initial packet for unknown connection {}",
253                dst_cid
254            );
255            None
256        } else if !event.first_decode.is_initial()
257            && self.local_cid_generator.validate(dst_cid).is_err()
258        {
259            debug!("dropping packet with invalid CID");
260            None
261        } else if dst_cid.is_empty() {
262            trace!("dropping unrecognized short packet without ID");
263            None
264        } else {
265            // If we got this far, we're receiving a seemingly valid packet for an unknown
266            // connection. Send a stateless reset if possible.
267            self.stateless_reset(now, datagram_len, network_path, dst_cid, buf)
268                .map(DatagramEvent::Response)
269        }
270    }
271
272    /// Builds a stateless reset packet to respond with
273    fn stateless_reset(
274        &mut self,
275        now: Instant,
276        inciting_dgram_len: usize,
277        network_path: FourTuple,
278        dst_cid: ConnectionId,
279        buf: &mut Vec<u8>,
280    ) -> Option<Transmit> {
281        if self
282            .last_stateless_reset
283            .is_some_and(|last| last + self.config.min_reset_interval > now)
284        {
285            debug!("ignoring unexpected packet within minimum stateless reset interval");
286            return None;
287        }
288
289        /// Minimum amount of padding for the stateless reset to look like a short-header packet
290        const MIN_PADDING_LEN: usize = 5;
291
292        // Prevent amplification attacks and reset loops by ensuring we pad to at most 1 byte
293        // smaller than the inciting packet.
294        let max_padding_len = match inciting_dgram_len.checked_sub(RESET_TOKEN_SIZE) {
295            Some(headroom) if headroom > MIN_PADDING_LEN => headroom - 1,
296            _ => {
297                debug!(
298                    "ignoring unexpected {} byte packet: not larger than minimum stateless reset size",
299                    inciting_dgram_len
300                );
301                return None;
302            }
303        };
304
305        debug!(%dst_cid, %network_path.remote, "sending stateless reset");
306        self.last_stateless_reset = Some(now);
307        // Resets with at least this much padding can't possibly be distinguished from real packets
308        const IDEAL_MIN_PADDING_LEN: usize = MIN_PADDING_LEN + MAX_CID_SIZE;
309        let padding_len = if max_padding_len <= IDEAL_MIN_PADDING_LEN {
310            max_padding_len
311        } else {
312            self.rng
313                .random_range(IDEAL_MIN_PADDING_LEN..max_padding_len)
314        };
315        buf.reserve(padding_len + RESET_TOKEN_SIZE);
316        buf.resize(padding_len, 0);
317        self.rng.fill_bytes(&mut buf[0..padding_len]);
318        buf[0] = 0b0100_0000 | (buf[0] >> 2);
319        buf.extend_from_slice(&ResetToken::new(&*self.config.reset_key, dst_cid));
320
321        debug_assert!(buf.len() < inciting_dgram_len);
322
323        Some(Transmit {
324            destination: network_path.remote,
325            ecn: None,
326            size: buf.len(),
327            segment_size: None,
328            src_ip: network_path.local_ip,
329        })
330    }
331
332    /// Initiate a connection
333    pub fn connect(
334        &mut self,
335        now: Instant,
336        config: ClientConfig,
337        remote: SocketAddr,
338        server_name: &str,
339    ) -> Result<(ConnectionHandle, Connection), ConnectError> {
340        if self.cids_exhausted() {
341            return Err(ConnectError::CidsExhausted);
342        }
343        if remote.port() == 0 || remote.ip().is_unspecified() {
344            return Err(ConnectError::InvalidRemoteAddress(remote));
345        }
346        if !self.config.supported_versions.contains(&config.version) {
347            return Err(ConnectError::UnsupportedVersion);
348        }
349
350        let remote_id = (config.initial_dst_cid_provider)();
351        trace!(initial_dcid = %remote_id);
352
353        let ch = ConnectionHandle(self.connections.vacant_key());
354        let local_cid = self.new_cid(ch, PathId::ZERO);
355        let params = TransportParameters::new(
356            &config.transport,
357            &self.config,
358            self.local_cid_generator.as_ref(),
359            local_cid,
360            None,
361            &mut self.rng,
362        );
363        let tls = config
364            .crypto
365            .start_session(config.version, server_name, &params)?;
366
367        let conn = self.add_connection(
368            ch,
369            config.version,
370            remote_id,
371            local_cid,
372            remote_id,
373            FourTuple {
374                remote,
375                local_ip: None,
376            },
377            now,
378            tls,
379            config.transport,
380            SideArgs::Client {
381                token_store: config.token_store,
382                server_name: server_name.into(),
383            },
384            &params,
385        );
386        Ok((ch, conn))
387    }
388
389    /// Generates new CIDs and creates message to send to the connection state
390    fn send_new_identifiers(
391        &mut self,
392        path_id: PathId,
393        now: Instant,
394        ch: ConnectionHandle,
395        num: u64,
396    ) -> ConnectionEvent {
397        let mut ids = vec![];
398        for _ in 0..num {
399            let id = self.new_cid(ch, path_id);
400            let cid_meta = self.connections[ch].local_cids.entry(path_id).or_default();
401            let sequence = cid_meta.issued;
402            cid_meta.issued += 1;
403            cid_meta.cids.insert(sequence, id);
404            ids.push(IssuedCid {
405                path_id,
406                sequence,
407                id,
408                reset_token: ResetToken::new(&*self.config.reset_key, id),
409            });
410        }
411        ConnectionEvent(ConnectionEventInner::NewIdentifiers(
412            ids,
413            now,
414            self.local_cid_generator.cid_len(),
415            self.local_cid_generator.cid_lifetime(),
416        ))
417    }
418
419    /// Generate a connection ID for `ch`
420    fn new_cid(&mut self, ch: ConnectionHandle, path_id: PathId) -> ConnectionId {
421        loop {
422            let cid = self.local_cid_generator.generate_cid();
423            if cid.is_empty() {
424                // Zero-length CID; nothing to track
425                debug_assert_eq!(self.local_cid_generator.cid_len(), 0);
426                return cid;
427            }
428            if let hash_map::Entry::Vacant(e) = self.index.connection_ids.entry(cid) {
429                e.insert((ch, path_id));
430                break cid;
431            }
432        }
433    }
434
435    fn handle_first_packet(
436        &mut self,
437        datagram_len: usize,
438        event: DatagramConnectionEvent,
439        network_path: FourTuple,
440        buf: &mut Vec<u8>,
441    ) -> Option<DatagramEvent> {
442        let dst_cid = event.first_decode.dst_cid();
443        let header = event.first_decode.initial_header().unwrap();
444
445        let Some(server_config) = &self.server_config else {
446            debug!("packet for unrecognized connection {}", dst_cid);
447            return self
448                .stateless_reset(event.now, datagram_len, network_path, dst_cid, buf)
449                .map(DatagramEvent::Response);
450        };
451
452        if datagram_len < MIN_INITIAL_SIZE as usize {
453            debug!("ignoring short initial for connection {}", dst_cid);
454            return None;
455        }
456
457        // Saturation only happens under heavy load, where deriving initial keys per Initial just to
458        // reply with CONNECTION_REFUSED would starve packet processing for existing connections.
459        if self.cids_exhausted() || self.incoming_buffers.len() >= server_config.max_incoming {
460            debug!(
461                "ignoring initial for connection {} due to saturation",
462                dst_cid
463            );
464            return None;
465        }
466
467        let crypto = match server_config.crypto.initial_keys(header.version, dst_cid) {
468            Ok(keys) => keys,
469            Err(UnsupportedVersion) => {
470                // This probably indicates that the user set supported_versions incorrectly in
471                // `EndpointConfig`.
472                debug!(
473                    "ignoring initial packet version {:#x} unsupported by cryptographic layer",
474                    header.version
475                );
476                return None;
477            }
478        };
479
480        if let Err(reason) = self.early_validate_first_packet(header) {
481            return Some(DatagramEvent::Response(self.initial_close(
482                header.version,
483                network_path,
484                &crypto,
485                header.src_cid,
486                reason,
487                buf,
488            )));
489        }
490
491        let packet = match event.first_decode.finish(Some(&*crypto.header.remote)) {
492            Ok(packet) => packet,
493            Err(e) => {
494                trace!("unable to decode initial packet: {}", e);
495                return None;
496            }
497        };
498
499        if !packet.reserved_bits_valid() {
500            debug!("dropping connection attempt with invalid reserved bits");
501            return None;
502        }
503
504        let Header::Initial(header) = packet.header else {
505            panic!("non-initial packet in handle_first_packet()");
506        };
507
508        let server_config = self.server_config.as_ref().unwrap().clone();
509
510        let token = match IncomingToken::from_header(&header, &server_config, network_path.remote) {
511            Ok(token) => token,
512            Err(InvalidRetryTokenError) => {
513                debug!("rejecting invalid retry token");
514                return Some(DatagramEvent::Response(self.initial_close(
515                    header.version,
516                    network_path,
517                    &crypto,
518                    header.src_cid,
519                    TransportError::INVALID_TOKEN(""),
520                    buf,
521                )));
522            }
523        };
524
525        let incoming_idx = self.incoming_buffers.insert(IncomingBuffer::default());
526        self.index
527            .insert_initial_incoming(header.dst_cid, incoming_idx);
528
529        Some(DatagramEvent::NewConnection(Incoming {
530            received_at: event.now,
531            network_path,
532            ecn: event.ecn,
533            packet: InitialPacket {
534                header,
535                header_data: packet.header_data,
536                payload: packet.payload,
537            },
538            rest: event.remaining,
539            crypto,
540            token,
541            incoming_idx,
542            improper_drop_warner: IncomingImproperDropWarner,
543        }))
544    }
545
546    /// Attempt to accept this incoming connection (an error may still occur)
547    // box err to avoid clippy::result_large_err
548    pub fn accept(
549        &mut self,
550        mut incoming: Incoming,
551        now: Instant,
552        buf: &mut Vec<u8>,
553        server_config: Option<Arc<ServerConfig>>,
554    ) -> Result<(ConnectionHandle, Connection), Box<AcceptError>> {
555        let remote_address_validated = incoming.remote_address_validated();
556        incoming.improper_drop_warner.dismiss();
557        let incoming_buffer = self.incoming_buffers.remove(incoming.incoming_idx);
558        self.all_incoming_buffers_total_bytes -= incoming_buffer.total_bytes;
559
560        let packet_number = incoming.packet.header.number.expand(0);
561        let InitialHeader {
562            src_cid,
563            dst_cid,
564            version,
565            ..
566        } = incoming.packet.header;
567        let server_config =
568            server_config.unwrap_or_else(|| self.server_config.as_ref().unwrap().clone());
569
570        if server_config
571            .transport
572            .max_idle_timeout
573            .is_some_and(|timeout| {
574                incoming.received_at + Duration::from_millis(timeout.into()) <= now
575            })
576        {
577            debug!("abandoning accept of stale initial");
578            self.index.remove_initial(dst_cid);
579            return Err(Box::new(AcceptError {
580                cause: ConnectionError::TimedOut,
581                response: None,
582            }));
583        }
584
585        if self.cids_exhausted() {
586            debug!("refusing connection");
587            self.index.remove_initial(dst_cid);
588            return Err(Box::new(AcceptError {
589                cause: ConnectionError::CidsExhausted,
590                response: Some(self.initial_close(
591                    version,
592                    incoming.network_path,
593                    &incoming.crypto,
594                    src_cid,
595                    TransportError::CONNECTION_REFUSED(""),
596                    buf,
597                )),
598            }));
599        }
600
601        if incoming
602            .crypto
603            .packet
604            .remote
605            .decrypt(
606                PathId::ZERO,
607                packet_number,
608                &incoming.packet.header_data,
609                &mut incoming.packet.payload,
610            )
611            .is_err()
612        {
613            debug!(packet_number, "failed to authenticate initial packet");
614            self.index.remove_initial(dst_cid);
615            return Err(Box::new(AcceptError {
616                cause: TransportError::PROTOCOL_VIOLATION("authentication failed").into(),
617                response: None,
618            }));
619        };
620
621        let ch = ConnectionHandle(self.connections.vacant_key());
622        let local_cid = self.new_cid(ch, PathId::ZERO);
623        let mut params = TransportParameters::new(
624            &server_config.transport,
625            &self.config,
626            self.local_cid_generator.as_ref(),
627            local_cid,
628            Some(&server_config),
629            &mut self.rng,
630        );
631        params.stateless_reset_token = Some(ResetToken::new(&*self.config.reset_key, local_cid));
632        params.original_dst_cid = Some(incoming.token.orig_dst_cid);
633        params.retry_src_cid = incoming.token.retry_src_cid;
634        let mut pref_addr_cid = None;
635        if server_config.has_preferred_address() {
636            let cid = self.new_cid(ch, PathId::ZERO);
637            pref_addr_cid = Some(cid);
638            params.preferred_address = Some(PreferredAddress {
639                address_v4: server_config.preferred_address_v4,
640                address_v6: server_config.preferred_address_v6,
641                connection_id: cid,
642                stateless_reset_token: ResetToken::new(&*self.config.reset_key, cid),
643            });
644        }
645
646        let tls = server_config.crypto.start_session(version, &params);
647        let transport_config = server_config.transport.clone();
648        let mut conn = self.add_connection(
649            ch,
650            version,
651            dst_cid,
652            local_cid,
653            src_cid,
654            incoming.network_path,
655            incoming.received_at,
656            tls,
657            transport_config,
658            SideArgs::Server {
659                server_config,
660                pref_addr_cid,
661                path_validated: remote_address_validated,
662            },
663            &params,
664        );
665        self.index.insert_initial(dst_cid, ch);
666
667        match conn.handle_first_packet(
668            incoming.received_at,
669            incoming.network_path,
670            incoming.ecn,
671            packet_number,
672            incoming.packet,
673            incoming.rest,
674        ) {
675            Ok(()) => {
676                trace!(
677                    id = ch.0,
678                    icid = %dst_cid,
679                    network_path = %incoming.network_path,
680                    "new connection",
681                );
682
683                for event in incoming_buffer.datagrams {
684                    conn.handle_event(ConnectionEvent(ConnectionEventInner::Datagram(event)))
685                }
686
687                Ok((ch, conn))
688            }
689            Err(e) => {
690                debug!("handshake failed: {}", e);
691                self.handle_event(ch, EndpointEvent(EndpointEventInner::Drained));
692                let response = match e {
693                    ConnectionError::TransportError(ref e) => Some(self.initial_close(
694                        version,
695                        incoming.network_path,
696                        &incoming.crypto,
697                        src_cid,
698                        e.clone(),
699                        buf,
700                    )),
701                    _ => None,
702                };
703                Err(Box::new(AcceptError { cause: e, response }))
704            }
705        }
706    }
707
708    /// Check if we should refuse a connection attempt regardless of the packet's contents
709    fn early_validate_first_packet(
710        &mut self,
711        header: &ProtectedInitialHeader,
712    ) -> Result<(), TransportError> {
713        // RFC9000 §7.2 dictates that initial (client-chosen) destination CIDs must be at least 8
714        // bytes. If this is a Retry packet, then the length must instead match our usual CID
715        // length. If we ever issue non-Retry address validation tokens via `NEW_TOKEN`, then we'll
716        // also need to validate CID length for those after decoding the token.
717        if header.dst_cid.len() < 8
718            && (header.token_pos.is_empty()
719                || header.dst_cid.len() != self.local_cid_generator.cid_len())
720        {
721            debug!(
722                "rejecting connection due to invalid DCID length {}",
723                header.dst_cid.len()
724            );
725            return Err(TransportError::PROTOCOL_VIOLATION(
726                "invalid destination CID length",
727            ));
728        }
729
730        Ok(())
731    }
732
733    /// Reject this incoming connection attempt
734    pub fn refuse(&mut self, incoming: Incoming, buf: &mut Vec<u8>) -> Transmit {
735        self.clean_up_incoming(&incoming);
736        incoming.improper_drop_warner.dismiss();
737
738        trace!(?incoming.network_path, "refusing incoming");
739
740        self.initial_close(
741            incoming.packet.header.version,
742            incoming.network_path,
743            &incoming.crypto,
744            incoming.packet.header.src_cid,
745            TransportError::CONNECTION_REFUSED(""),
746            buf,
747        )
748    }
749
750    /// Respond with a retry packet, requiring the client to retry with address validation
751    ///
752    /// Errors if `incoming.may_retry()` is false.
753    pub fn retry(&mut self, incoming: Incoming, buf: &mut Vec<u8>) -> Result<Transmit, RetryError> {
754        if !incoming.may_retry() {
755            trace!(
756                ?incoming.network_path,
757                "not responding retry on incoming due to missing src CID"
758            );
759            return Err(RetryError(Box::new(incoming)));
760        }
761
762        trace!(?incoming.network_path, "responding retry on incoming");
763
764        self.clean_up_incoming(&incoming);
765        incoming.improper_drop_warner.dismiss();
766
767        let server_config = self.server_config.as_ref().unwrap();
768
769        // First Initial
770        // The peer will use this as the DCID of its following Initials. Initial DCIDs are
771        // looked up separately from Handshake/Data DCIDs, so there is no risk of collision
772        // with established connections. In the unlikely event that a collision occurs
773        // between two connections in the initial phase, both will fail fast and may be
774        // retried by the application layer.
775        let local_cid = self.local_cid_generator.generate_cid();
776
777        let payload = TokenPayload::Retry {
778            address: incoming.network_path.remote,
779            orig_dst_cid: incoming.packet.header.dst_cid,
780            issued: server_config.time_source.now(),
781        };
782        let token = Token::new(payload, &mut self.rng).encode(&*server_config.token_key);
783
784        let header = Header::Retry {
785            src_cid: local_cid,
786            dst_cid: incoming.packet.header.src_cid,
787            version: incoming.packet.header.version,
788        };
789
790        let encode = header.encode(buf);
791        buf.put_slice(&token);
792        buf.extend_from_slice(&server_config.crypto.retry_tag(
793            incoming.packet.header.version,
794            incoming.packet.header.dst_cid,
795            buf,
796        ));
797        encode.finish(buf, &*incoming.crypto.header.local, None);
798
799        Ok(Transmit {
800            destination: incoming.network_path.remote,
801            ecn: None,
802            size: buf.len(),
803            segment_size: None,
804            src_ip: incoming.network_path.local_ip,
805        })
806    }
807
808    /// Ignore this incoming connection attempt, not sending any packet in response
809    ///
810    /// Doing this actively, rather than merely dropping the [`Incoming`], is necessary to prevent
811    /// memory leaks due to state within [`Endpoint`] tracking the incoming connection.
812    pub fn ignore(&mut self, incoming: Incoming) {
813        self.clean_up_incoming(&incoming);
814        incoming.improper_drop_warner.dismiss();
815
816        trace!(?incoming.network_path, "ignoring incoming");
817    }
818
819    /// Clean up endpoint data structures associated with an `Incoming`.
820    fn clean_up_incoming(&mut self, incoming: &Incoming) {
821        self.index.remove_initial(incoming.packet.header.dst_cid);
822        let incoming_buffer = self.incoming_buffers.remove(incoming.incoming_idx);
823        self.all_incoming_buffers_total_bytes -= incoming_buffer.total_bytes;
824    }
825
826    fn add_connection(
827        &mut self,
828        ch: ConnectionHandle,
829        version: u32,
830        init_cid: ConnectionId,
831        local_cid: ConnectionId,
832        remote_cid: ConnectionId,
833        network_path: FourTuple,
834        now: Instant,
835        tls: Box<dyn crypto::Session>,
836        transport_config: Arc<TransportConfig>,
837        side_args: SideArgs,
838        // Only used for qlog.
839        params: &TransportParameters,
840    ) -> Connection {
841        let mut rng_seed = [0; 32];
842        self.rng.fill_bytes(&mut rng_seed);
843        let side = side_args.side();
844        let pref_addr_cid = side_args.pref_addr_cid();
845
846        let qlog =
847            transport_config.create_qlog_sink(side_args.side(), network_path.remote, init_cid, now);
848
849        qlog.emit_connection_started(
850            now,
851            local_cid,
852            remote_cid,
853            network_path.remote,
854            network_path.local_ip,
855            params,
856        );
857
858        let conn = Connection::new(
859            self.config.clone(),
860            transport_config,
861            init_cid,
862            local_cid,
863            remote_cid,
864            network_path,
865            tls,
866            self.local_cid_generator.as_ref(),
867            now,
868            version,
869            self.allow_mtud,
870            rng_seed,
871            side_args,
872            qlog,
873        );
874
875        let mut path_cids = PathLocalCids::default();
876        path_cids.cids.insert(path_cids.issued, local_cid);
877        path_cids.issued += 1;
878
879        if let Some(cid) = pref_addr_cid {
880            debug_assert_eq!(path_cids.issued, 1, "preferred address cid seq must be 1");
881            path_cids.cids.insert(path_cids.issued, cid);
882            path_cids.issued += 1;
883        }
884
885        let id = self.connections.insert(ConnectionMeta {
886            init_cid,
887            local_cids: FxHashMap::from_iter([(PathId::ZERO, path_cids)]),
888            network_path,
889            side,
890            reset_token: Default::default(),
891        });
892        debug_assert_eq!(id, ch.0, "connection handle allocation out of sync");
893
894        self.index.insert_conn(network_path, local_cid, ch, side);
895
896        conn
897    }
898
899    fn initial_close(
900        &mut self,
901        version: u32,
902        network_path: FourTuple,
903        crypto: &Keys,
904        remote_id: ConnectionId,
905        reason: TransportError,
906        buf: &mut Vec<u8>,
907    ) -> Transmit {
908        // We don't need to worry about CID collisions in initial closes because the peer
909        // shouldn't respond, and if it does, and the CID collides, we'll just drop the
910        // unexpected response.
911        let local_id = self.local_cid_generator.generate_cid();
912        let number = PacketNumber::U8(0);
913        let header = Header::Initial(InitialHeader {
914            dst_cid: remote_id,
915            src_cid: local_id,
916            number,
917            token: Bytes::new(),
918            version,
919        });
920
921        let partial_encode = header.encode(buf);
922        let max_len =
923            INITIAL_MTU as usize - partial_encode.header_len - crypto.packet.local.tag_len();
924        frame::Close::from(reason).encoder(max_len).encode(buf);
925        buf.resize(buf.len() + crypto.packet.local.tag_len(), 0);
926        partial_encode.finish(
927            buf,
928            &*crypto.header.local,
929            Some((0, Default::default(), &*crypto.packet.local)),
930        );
931        Transmit {
932            destination: network_path.remote,
933            ecn: None,
934            size: buf.len(),
935            segment_size: None,
936            src_ip: network_path.local_ip,
937        }
938    }
939
940    /// Access the configuration used by this endpoint
941    pub fn config(&self) -> &EndpointConfig {
942        &self.config
943    }
944
945    /// Number of connections that are currently open
946    pub fn open_connections(&self) -> usize {
947        self.connections.len()
948    }
949
950    /// Counter for the number of bytes currently used
951    /// in the buffers for Initial and 0-RTT messages for pending incoming connections
952    pub fn incoming_buffer_bytes(&self) -> u64 {
953        self.all_incoming_buffers_total_bytes
954    }
955
956    #[cfg(test)]
957    pub(crate) fn known_connections(&self) -> usize {
958        let x = self.connections.len();
959        debug_assert_eq!(x, self.index.connection_ids_initial.len());
960        // Not all connections have known reset tokens
961        debug_assert!(x >= self.index.connection_reset_tokens.0.len());
962        // Not all connections have unique remotes, and 0-length CIDs might not be in use.
963        debug_assert!(x >= self.index.incoming_connection_remotes.len());
964        debug_assert!(x >= self.index.outgoing_connection_remotes.len());
965        x
966    }
967
968    #[cfg(test)]
969    pub(crate) fn known_cids(&self) -> usize {
970        self.index.connection_ids.len()
971    }
972
973    /// Whether we've used up 3/4 of the available CID space
974    ///
975    /// We leave some space unused so that `new_cid` can be relied upon to finish quickly. We don't
976    /// bother to check when CID longer than 4 bytes are used because 2^40 connections is a lot.
977    fn cids_exhausted(&self) -> bool {
978        let cid_len = self.local_cid_generator.cid_len();
979        if cid_len == 0 || cid_len > 4 {
980            return false;
981        }
982
983        // Keep this architecture-independent: on 32-bit targets, 2usize.pow(32) overflows.
984        let bits = (cid_len * 8) as u32;
985        let space = 1u64 << bits;
986        let reserve = 1u64 << (bits - 2);
987        let len = self.index.connection_ids.len() as u64;
988
989        len > (space - reserve)
990    }
991}
992
993impl fmt::Debug for Endpoint {
994    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
995        fmt.debug_struct("Endpoint")
996            .field("rng", &self.rng)
997            .field("index", &self.index)
998            .field("connections", &self.connections)
999            .field("config", &self.config)
1000            .field("server_config", &self.server_config)
1001            // incoming_buffers too large
1002            .field("incoming_buffers.len", &self.incoming_buffers.len())
1003            .field(
1004                "all_incoming_buffers_total_bytes",
1005                &self.all_incoming_buffers_total_bytes,
1006            )
1007            .finish()
1008    }
1009}
1010
1011/// Buffered Initial and 0-RTT messages for a pending incoming connection
1012#[derive(Default)]
1013struct IncomingBuffer {
1014    datagrams: Vec<DatagramConnectionEvent>,
1015    total_bytes: u64,
1016}
1017
1018/// Part of protocol state incoming datagrams can be routed to
1019#[derive(Copy, Clone, Debug)]
1020enum RouteDatagramTo {
1021    Incoming(usize),
1022    Connection(ConnectionHandle, PathId),
1023}
1024
1025/// Maps packets to existing connections
1026#[derive(Default, Debug)]
1027struct ConnectionIndex {
1028    /// Identifies connections based on the initial DCID the peer utilized
1029    ///
1030    /// Uses a standard `HashMap` to protect against hash collision attacks.
1031    ///
1032    /// Used by the server, not the client.
1033    connection_ids_initial: HashMap<ConnectionId, RouteDatagramTo>,
1034    /// Identifies connections based on locally created CIDs
1035    ///
1036    /// Uses a cheaper hash function since keys are locally created
1037    connection_ids: FxHashMap<ConnectionId, (ConnectionHandle, PathId)>,
1038    /// Identifies incoming connections with zero-length CIDs
1039    ///
1040    /// Uses a standard `HashMap` to protect against hash collision attacks.
1041    incoming_connection_remotes: HashMap<FourTuple, ConnectionHandle>,
1042    /// Identifies outgoing connections with zero-length CIDs
1043    ///
1044    /// We don't yet support explicit source addresses for client connections, and zero-length CIDs
1045    /// require a unique 4-tuple, so at most one client connection with zero-length local CIDs
1046    /// may be established per remote. We must omit the local address from the key because we don't
1047    /// necessarily know what address we're sending from, and hence receiving at.
1048    ///
1049    /// Uses a standard `HashMap` to protect against hash collision attacks.
1050    // TODO(matheus23): It's possible this could be changed now that we track the full 4-tuple on the client side, too.
1051    outgoing_connection_remotes: HashMap<SocketAddr, ConnectionHandle>,
1052    /// Reset tokens provided by the peer for the CID each connection is currently sending to
1053    ///
1054    /// Incoming stateless resets do not have correct CIDs, so we need this to identify the correct
1055    /// recipient, if any.
1056    connection_reset_tokens: ResetTokenTable,
1057}
1058
1059impl ConnectionIndex {
1060    /// Associate an incoming connection with its initial destination CID
1061    fn insert_initial_incoming(&mut self, dst_cid: ConnectionId, incoming_key: usize) {
1062        if dst_cid.is_empty() {
1063            return;
1064        }
1065        self.connection_ids_initial
1066            .insert(dst_cid, RouteDatagramTo::Incoming(incoming_key));
1067    }
1068
1069    /// Remove an association with an initial destination CID
1070    fn remove_initial(&mut self, dst_cid: ConnectionId) {
1071        if dst_cid.is_empty() {
1072            return;
1073        }
1074        let removed = self.connection_ids_initial.remove(&dst_cid);
1075        debug_assert!(removed.is_some());
1076    }
1077
1078    /// Associate a connection with its initial destination CID
1079    fn insert_initial(&mut self, dst_cid: ConnectionId, connection: ConnectionHandle) {
1080        if dst_cid.is_empty() {
1081            return;
1082        }
1083        self.connection_ids_initial.insert(
1084            dst_cid,
1085            RouteDatagramTo::Connection(connection, PathId::ZERO),
1086        );
1087    }
1088
1089    /// Associate a connection with its first locally-chosen destination CID if used, or otherwise
1090    /// its current 4-tuple
1091    fn insert_conn(
1092        &mut self,
1093        network_path: FourTuple,
1094        dst_cid: ConnectionId,
1095        connection: ConnectionHandle,
1096        side: Side,
1097    ) {
1098        match dst_cid.len() {
1099            0 => match side {
1100                Side::Server => {
1101                    self.incoming_connection_remotes
1102                        .insert(network_path, connection);
1103                }
1104                Side::Client => {
1105                    self.outgoing_connection_remotes
1106                        .insert(network_path.remote, connection);
1107                }
1108            },
1109            _ => {
1110                self.connection_ids
1111                    .insert(dst_cid, (connection, PathId::ZERO));
1112            }
1113        }
1114    }
1115
1116    /// Discard a connection ID
1117    fn retire(&mut self, dst_cid: ConnectionId) {
1118        self.connection_ids.remove(&dst_cid);
1119    }
1120
1121    /// Remove all references to a connection
1122    fn remove(&mut self, conn: &ConnectionMeta) {
1123        if conn.side.is_server() {
1124            self.remove_initial(conn.init_cid);
1125        }
1126        for cid in conn
1127            .local_cids
1128            .values()
1129            .flat_map(|pcids| pcids.cids.values())
1130        {
1131            self.connection_ids.remove(cid);
1132        }
1133        self.incoming_connection_remotes.remove(&conn.network_path);
1134        self.outgoing_connection_remotes
1135            .remove(&conn.network_path.remote);
1136        for (remote, token) in conn.reset_token.values() {
1137            self.connection_reset_tokens.remove(*remote, *token);
1138        }
1139    }
1140
1141    /// Find the existing connection that `datagram` should be routed to, if any
1142    fn get(&self, network_path: &FourTuple, datagram: &PartialDecode) -> Option<RouteDatagramTo> {
1143        if !datagram.dst_cid().is_empty()
1144            && let Some(&(ch, path_id)) = self.connection_ids.get(&datagram.dst_cid())
1145        {
1146            return Some(RouteDatagramTo::Connection(ch, path_id));
1147        }
1148        if (datagram.is_initial() || datagram.is_0rtt())
1149            && let Some(&ch) = self.connection_ids_initial.get(&datagram.dst_cid())
1150        {
1151            return Some(ch);
1152        }
1153        if datagram.dst_cid().is_empty() {
1154            if let Some(&ch) = self.incoming_connection_remotes.get(network_path) {
1155                // Never multipath because QUIC-MULTIPATH 1.1 mandates the use of non-zero
1156                // length CIDs.  So this is always PathId::ZERO.
1157                return Some(RouteDatagramTo::Connection(ch, PathId::ZERO));
1158            }
1159            if let Some(&ch) = self.outgoing_connection_remotes.get(&network_path.remote) {
1160                // Like above, QUIC-MULTIPATH 1.1 mandates the use of non-zero length CIDs.
1161                return Some(RouteDatagramTo::Connection(ch, PathId::ZERO));
1162            }
1163        }
1164        let data = datagram.data();
1165        if data.len() < RESET_TOKEN_SIZE {
1166            return None;
1167        }
1168        // For stateless resets the PathId is meaningless since it closes the entire
1169        // connection regardless of path.  So use PathId::ZERO.
1170        self.connection_reset_tokens
1171            .get(network_path.remote, &data[data.len() - RESET_TOKEN_SIZE..])
1172            .cloned()
1173            .map(|ch| RouteDatagramTo::Connection(ch, PathId::ZERO))
1174    }
1175}
1176
1177#[derive(Debug)]
1178pub(crate) struct ConnectionMeta {
1179    init_cid: ConnectionId,
1180    /// Locally issues CIDs for each path
1181    local_cids: FxHashMap<PathId, PathLocalCids>,
1182    /// Remote/local addresses the connection began with
1183    ///
1184    /// Only needed to support connections with zero-length CIDs, which cannot migrate, so we don't
1185    /// bother keeping it up to date.
1186    network_path: FourTuple,
1187    side: Side,
1188    /// Reset tokens provided by the peer for CIDs we're currently sending to
1189    ///
1190    /// Since each reset token is for a CID, it is also for a fixed remote address which is
1191    /// also stored. This allows us to look up which reset tokens we might expect from a
1192    /// given remote address, see [`ResetTokenTable`].
1193    ///
1194    /// Each path has its own active CID. We use the [`PathId`] as a unique index, allowing
1195    /// us to retire the reset token when a path is abandoned.
1196    // TODO(matheus23): Should be migrated to make reset tokens per 4-tuple instead of per remote addr
1197    reset_token: FxHashMap<PathId, (SocketAddr, ResetToken)>,
1198}
1199
1200/// Local connection IDs for a single path
1201#[derive(Debug, Default)]
1202struct PathLocalCids {
1203    /// Number of connection IDs that have been issued in (PATH_)NEW_CONNECTION_ID frames
1204    ///
1205    /// Another way of saying this is that this is the next sequence number to be issued.
1206    issued: u64,
1207    /// Issues CIDs indexed by their sequence number.
1208    cids: FxHashMap<u64, ConnectionId>,
1209}
1210
1211/// Internal identifier for a `Connection` currently associated with an endpoint
1212#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
1213pub struct ConnectionHandle(pub usize);
1214
1215impl From<ConnectionHandle> for usize {
1216    fn from(x: ConnectionHandle) -> Self {
1217        x.0
1218    }
1219}
1220
1221impl Index<ConnectionHandle> for Slab<ConnectionMeta> {
1222    type Output = ConnectionMeta;
1223    fn index(&self, ch: ConnectionHandle) -> &ConnectionMeta {
1224        &self[ch.0]
1225    }
1226}
1227
1228impl IndexMut<ConnectionHandle> for Slab<ConnectionMeta> {
1229    fn index_mut(&mut self, ch: ConnectionHandle) -> &mut ConnectionMeta {
1230        &mut self[ch.0]
1231    }
1232}
1233
1234/// Event resulting from processing a single datagram
1235pub enum DatagramEvent {
1236    /// The datagram is redirected to its `Connection`
1237    ConnectionEvent(ConnectionHandle, ConnectionEvent),
1238    /// The datagram may result in starting a new `Connection`
1239    NewConnection(Incoming),
1240    /// Response generated directly by the endpoint
1241    Response(Transmit),
1242}
1243
1244/// An incoming connection for which the server has not yet begun its part of the handshake.
1245#[derive(derive_more::Debug)]
1246pub struct Incoming {
1247    #[debug(skip)]
1248    received_at: Instant,
1249    network_path: FourTuple,
1250    ecn: Option<EcnCodepoint>,
1251    #[debug(skip)]
1252    packet: InitialPacket,
1253    #[debug(skip)]
1254    rest: Option<BytesMut>,
1255    #[debug(skip)]
1256    crypto: Keys,
1257    token: IncomingToken,
1258    incoming_idx: usize,
1259    #[debug(skip)]
1260    improper_drop_warner: IncomingImproperDropWarner,
1261}
1262
1263impl Incoming {
1264    /// The local IP address which was used when the peer established the connection
1265    pub fn local_ip(&self) -> Option<IpAddr> {
1266        self.network_path.local_ip
1267    }
1268
1269    /// The peer's UDP address
1270    pub fn remote_address(&self) -> SocketAddr {
1271        self.network_path.remote
1272    }
1273
1274    /// Whether the socket address that is initiating this connection has been validated
1275    ///
1276    /// This means that the sender of the initial packet has proved that they can receive traffic
1277    /// sent to `self.remote_address()`.
1278    ///
1279    /// If `self.remote_address_validated()` is false, `self.may_retry()` is guaranteed to be true.
1280    /// The inverse is not guaranteed.
1281    pub fn remote_address_validated(&self) -> bool {
1282        self.token.validated
1283    }
1284
1285    /// Whether it is legal to respond with a retry packet
1286    ///
1287    /// If `self.remote_address_validated()` is false, `self.may_retry()` is guaranteed to be true.
1288    /// The inverse is not guaranteed.
1289    pub fn may_retry(&self) -> bool {
1290        self.token.retry_src_cid.is_none()
1291    }
1292
1293    /// The original destination connection ID sent by the client
1294    pub fn orig_dst_cid(&self) -> ConnectionId {
1295        self.token.orig_dst_cid
1296    }
1297
1298    /// Decrypt the Initial packet payload
1299    ///
1300    /// This clones and decrypts the packet payload (~1200 bytes).
1301    /// Can be used to extract information from the TLS ClientHello without completing the handshake.
1302    pub fn decrypt(&self) -> Option<DecryptedInitial> {
1303        let packet_number = self.packet.header.number.expand(0);
1304        let mut payload = self.packet.payload.clone();
1305        self.crypto
1306            .packet
1307            .remote
1308            .decrypt(
1309                PathId::ZERO,
1310                packet_number,
1311                &self.packet.header_data,
1312                &mut payload,
1313            )
1314            .ok()?;
1315        Some(DecryptedInitial(payload.freeze()))
1316    }
1317}
1318
1319/// Decrypted payload of a QUIC Initial packet
1320///
1321/// Obtained via [`Incoming::decrypt`]. Can be used to extract information from
1322/// the TLS ClientHello without completing the handshake.
1323pub struct DecryptedInitial(Bytes);
1324
1325impl DecryptedInitial {
1326    /// Best-effort extraction of the ALPN protocols from the TLS ClientHello
1327    ///
1328    /// Parses the CRYPTO frames to extract the ALPN extension. This is intended
1329    /// for routing and filtering; it is not guaranteed to succeed if the
1330    /// ClientHello spans multiple packets. Returns `None` if parsing fails.
1331    pub fn alpns(&self) -> Option<IncomingAlpns> {
1332        let frames = frame::Iter::new(self.0.clone()).ok()?;
1333        let mut first = None;
1334        let mut rest = Vec::new();
1335        for frame in frames {
1336            match frame {
1337                Ok(frame::Frame::Crypto(crypto)) => match first {
1338                    None => first = Some(crypto),
1339                    Some(_) => rest.push(crypto),
1340                },
1341                Err(_) => return None,
1342                _ => {}
1343            }
1344        }
1345        let first = first?;
1346
1347        // Fast path: single CRYPTO frame at offset 0 (no extra allocation)
1348        if rest.is_empty() && first.offset == 0 {
1349            let data = find_alpn_data(&first.data).ok()?;
1350            return Some(IncomingAlpns { data, pos: 0 });
1351        }
1352
1353        // Slow path: reassemble multiple CRYPTO frames
1354        rest.push(first);
1355        let source = assemble_crypto_frames(&mut rest)?;
1356        let data = find_alpn_data(&source).ok()?;
1357        Some(IncomingAlpns { data, pos: 0 })
1358    }
1359}
1360
1361/// TLS handshake type for ClientHello messages
1362/// <https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2>
1363const TLS_HANDSHAKE_TYPE_CLIENT_HELLO: u8 = 0x01;
1364/// TLS extension type for Application-Layer Protocol Negotiation
1365/// <https://www.rfc-editor.org/rfc/rfc7301#section-3.1>
1366const TLS_EXTENSION_TYPE_ALPN: u16 = 0x0010;
1367/// Size of the fixed-length fields in a ClientHello (client_version + random)
1368/// <https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2>
1369const TLS_CLIENT_HELLO_FIXED_LEN: usize = 2 + 32;
1370
1371/// Iterator over ALPN protocol names from a TLS ClientHello
1372///
1373/// Yields protocol names as [`Bytes`] slices. On the common fast path (single
1374/// CRYPTO frame), the only allocation is the payload clone for decryption.
1375pub struct IncomingAlpns {
1376    data: Bytes,
1377    pos: usize,
1378}
1379
1380impl Iterator for IncomingAlpns {
1381    type Item = Result<Bytes, UnexpectedEnd>;
1382
1383    fn next(&mut self) -> Option<Self::Item> {
1384        if self.pos >= self.data.len() {
1385            return None;
1386        }
1387        let len = self.data[self.pos] as usize;
1388        self.pos += 1;
1389        if self.pos + len > self.data.len() {
1390            return Some(Err(UnexpectedEnd));
1391        }
1392        let proto = self.data.slice(self.pos..self.pos + len);
1393        self.pos += len;
1394        Some(Ok(proto))
1395    }
1396}
1397
1398/// Sort CRYPTO frames by offset and concatenate into a contiguous `Bytes`
1399///
1400/// Returns `None` if there are gaps in the stream.
1401fn assemble_crypto_frames(frames: &mut [frame::Crypto]) -> Option<Bytes> {
1402    frames.sort_by_key(|f| f.offset);
1403    let capacity = frames.iter().map(|f| f.data.len()).sum();
1404    let mut buf = Vec::with_capacity(capacity);
1405    for f in frames.iter() {
1406        let start = f.offset as usize;
1407        if start > buf.len() {
1408            return None;
1409        }
1410        let end = start + f.data.len();
1411        if end > buf.len() {
1412            buf.extend_from_slice(&f.data[buf.len() - start..]);
1413        }
1414    }
1415    Some(Bytes::from(buf))
1416}
1417
1418/// Locate the raw ALPN protocol list data within a TLS ClientHello message
1419///
1420/// Parses the ClientHello in `source` and returns a [`Bytes`] containing the
1421/// u8-length-prefixed protocol names (after the outer ProtocolNameList u16
1422/// length prefix). The returned `Bytes` is a zero-copy slice of `source`.
1423fn find_alpn_data(source: &Bytes) -> Result<Bytes, UnexpectedEnd> {
1424    let mut r = &**source;
1425
1426    if u8::decode(&mut r)? != TLS_HANDSHAKE_TYPE_CLIENT_HELLO {
1427        return Err(UnexpectedEnd);
1428    }
1429
1430    // Handshake message length (u24), scopes the remainder
1431    let len = decode_u24(&mut r)?;
1432    let mut body = take(&mut r, len)?;
1433
1434    // Client version + random
1435    skip(&mut body, TLS_CLIENT_HELLO_FIXED_LEN)?;
1436
1437    // Session ID, cipher suites, compression methods
1438    skip_u8_prefixed(&mut body)?;
1439    skip_u16_prefixed(&mut body)?;
1440    skip_u8_prefixed(&mut body)?;
1441
1442    // Extensions
1443    let mut exts = take_u16_prefixed(&mut body)?;
1444    while exts.has_remaining() {
1445        let ext_type = u16::decode(&mut exts)?;
1446        let ext_data = take_u16_prefixed(&mut exts)?;
1447        if ext_type == TLS_EXTENSION_TYPE_ALPN {
1448            let list = take_u16_prefixed(&mut &*ext_data)?;
1449            return Ok(source.slice_ref(list));
1450        }
1451    }
1452    Err(UnexpectedEnd)
1453}
1454
1455/// Decode a big-endian u24 as usize
1456fn decode_u24(r: &mut &[u8]) -> Result<usize, UnexpectedEnd> {
1457    let a = u8::decode(r)?;
1458    let b = u8::decode(r)?;
1459    let c = u8::decode(r)?;
1460    Ok(u32::from_be_bytes([0, a, b, c]) as usize)
1461}
1462
1463/// Take `len` bytes from the front and return them as a sub-slice
1464fn take<'a>(r: &mut &'a [u8], len: usize) -> Result<&'a [u8], UnexpectedEnd> {
1465    if r.remaining() < len {
1466        return Err(UnexpectedEnd);
1467    }
1468    let data = &r[..len];
1469    r.advance(len);
1470    Ok(data)
1471}
1472
1473/// Read a u16 length prefix and return the sub-slice it covers
1474fn take_u16_prefixed<'a>(r: &mut &'a [u8]) -> Result<&'a [u8], UnexpectedEnd> {
1475    let len = u16::decode(r)? as usize;
1476    take(r, len)
1477}
1478
1479/// Advance past `n` bytes
1480fn skip(r: &mut &[u8], len: usize) -> Result<(), UnexpectedEnd> {
1481    take(r, len)?;
1482    Ok(())
1483}
1484
1485/// Skip a u8-length-prefixed field
1486fn skip_u8_prefixed(r: &mut &[u8]) -> Result<(), UnexpectedEnd> {
1487    let len = u8::decode(r)? as usize;
1488    skip(r, len)
1489}
1490
1491/// Skip a u16-length-prefixed field
1492fn skip_u16_prefixed(r: &mut &[u8]) -> Result<(), UnexpectedEnd> {
1493    let len = u16::decode(r)? as usize;
1494    skip(r, len)
1495}
1496
1497struct IncomingImproperDropWarner;
1498
1499impl IncomingImproperDropWarner {
1500    fn dismiss(self) {
1501        mem::forget(self);
1502    }
1503}
1504
1505impl Drop for IncomingImproperDropWarner {
1506    fn drop(&mut self) {
1507        warn!(
1508            "noq_proto::Incoming dropped without passing to Endpoint::accept/refuse/retry/ignore \
1509               (may cause memory leak and eventual inability to accept new connections)"
1510        );
1511    }
1512}
1513
1514/// Errors in the parameters being used to create a new connection
1515///
1516/// These arise before any I/O has been performed.
1517#[derive(Debug, Error, Clone, PartialEq, Eq)]
1518pub enum ConnectError {
1519    /// The endpoint can no longer create new connections
1520    ///
1521    /// Indicates that a necessary component of the endpoint has been dropped or otherwise disabled.
1522    #[error("endpoint stopping")]
1523    EndpointStopping,
1524    /// The connection could not be created because not enough of the CID space is available
1525    ///
1526    /// Try using longer connection IDs
1527    #[error("CIDs exhausted")]
1528    CidsExhausted,
1529    /// The given server name was malformed
1530    #[error("invalid server name: {0}")]
1531    InvalidServerName(String),
1532    /// The remote [`SocketAddr`] supplied was malformed
1533    ///
1534    /// Examples include attempting to connect to port 0, or using an inappropriate address family.
1535    #[error("invalid remote address: {0}")]
1536    InvalidRemoteAddress(SocketAddr),
1537    /// No default client configuration was set up
1538    ///
1539    /// Use `Endpoint::connect_with` to specify a client configuration.
1540    #[error("no default client config")]
1541    NoDefaultClientConfig,
1542    /// The local endpoint does not support the QUIC version specified in the client configuration
1543    #[error("unsupported QUIC version")]
1544    UnsupportedVersion,
1545}
1546
1547/// Error type for attempting to accept an [`Incoming`]
1548#[derive(Debug)]
1549pub struct AcceptError {
1550    /// Underlying error describing reason for failure
1551    pub cause: ConnectionError,
1552    /// Optional response to transmit back
1553    pub response: Option<Transmit>,
1554}
1555
1556/// Error for attempting to retry an [`Incoming`] which already bears a token from a previous retry
1557#[derive(Debug, Error)]
1558#[error("retry() with validated Incoming")]
1559pub struct RetryError(Box<Incoming>);
1560
1561impl RetryError {
1562    /// Get the [`Incoming`]
1563    pub fn into_incoming(self) -> Incoming {
1564        *self.0
1565    }
1566}
1567
1568/// Reset Tokens which are associated with peer socket addresses
1569///
1570/// The standard `HashMap` is used since both `SocketAddr` and `ResetToken` are
1571/// peer generated and might be usable for hash collision attacks.
1572#[derive(Default, Debug)]
1573struct ResetTokenTable(HashMap<SocketAddr, HashMap<ResetToken, ConnectionHandle>>);
1574
1575impl ResetTokenTable {
1576    fn insert(&mut self, remote: SocketAddr, token: ResetToken, ch: ConnectionHandle) -> bool {
1577        self.0
1578            .entry(remote)
1579            .or_default()
1580            .insert(token, ch)
1581            .is_some()
1582    }
1583
1584    fn remove(&mut self, remote: SocketAddr, token: ResetToken) {
1585        use std::collections::hash_map::Entry;
1586        match self.0.entry(remote) {
1587            Entry::Vacant(_) => {}
1588            Entry::Occupied(mut e) => {
1589                e.get_mut().remove(&token);
1590                if e.get().is_empty() {
1591                    e.remove_entry();
1592                }
1593            }
1594        }
1595    }
1596
1597    fn get(&self, remote: SocketAddr, token: &[u8]) -> Option<&ConnectionHandle> {
1598        let token = ResetToken::from(<[u8; RESET_TOKEN_SIZE]>::try_from(token).ok()?);
1599        self.0.get(&remote)?.get(&token)
1600    }
1601}
1602
1603#[cfg(test)]
1604mod tests {
1605    use super::*;
1606
1607    #[test]
1608    fn assemble_contiguous() {
1609        let data = b"hello world";
1610        let mut frames = vec![
1611            frame::Crypto {
1612                offset: 0,
1613                data: Bytes::from_static(&data[..5]),
1614            },
1615            frame::Crypto {
1616                offset: 5,
1617                data: Bytes::from_static(&data[5..]),
1618            },
1619        ];
1620        assert_eq!(&assemble_crypto_frames(&mut frames).unwrap()[..], &data[..]);
1621    }
1622
1623    #[test]
1624    fn assemble_out_of_order() {
1625        let data = b"hello world";
1626        let mut frames = vec![
1627            frame::Crypto {
1628                offset: 5,
1629                data: Bytes::from_static(&data[5..]),
1630            },
1631            frame::Crypto {
1632                offset: 0,
1633                data: Bytes::from_static(&data[..5]),
1634            },
1635        ];
1636        assert_eq!(&assemble_crypto_frames(&mut frames).unwrap()[..], &data[..]);
1637    }
1638
1639    #[test]
1640    fn assemble_with_overlap() {
1641        let data = b"hello world";
1642        let mut frames = vec![
1643            frame::Crypto {
1644                offset: 0,
1645                data: Bytes::from_static(&data[..7]),
1646            },
1647            frame::Crypto {
1648                offset: 5,
1649                data: Bytes::from_static(&data[5..]),
1650            },
1651        ];
1652        assert_eq!(&assemble_crypto_frames(&mut frames).unwrap()[..], &data[..]);
1653    }
1654
1655    #[test]
1656    fn assemble_with_gap() {
1657        let mut frames = vec![frame::Crypto {
1658            offset: 10,
1659            data: Bytes::from_static(b"world"),
1660        }];
1661        assert!(assemble_crypto_frames(&mut frames).is_none());
1662    }
1663}