// Apply a wee bit of greasing here in picking a length between 8 and 20 bytes long. #[must_use] pubfn generate_initial() -> Self { let v = random::<1>()[0]; // Bias selection toward picking 8 (>50% of the time). let len: usize = max(8, 5 + (v & (v >> 4))).into(); Self::generate(len)
}
pubtrait ConnectionIdDecoder { /// Decodes a connection ID from the provided decoder. fn decode_cid<'a>(&self, dec: &mut Decoder<'a>) -> Option<ConnectionIdRef<'a>>;
}
pubtrait ConnectionIdGenerator: ConnectionIdDecoder { /// Generates a connection ID. This can return `None` if the generator /// is exhausted. fn generate_cid(&mutself) -> Option<ConnectionId>; /// Indicates whether the connection IDs are zero-length. /// If this returns true, `generate_cid` must always produce an empty value /// and never `None`. /// If this returns false, `generate_cid` must never produce an empty value, /// though it can return `None`. /// /// You should not need to implement this: if you want zero-length connection IDs, /// use `EmptyConnectionIdGenerator` instead. fn generates_empty_cids(&self) -> bool { false
} fn as_decoder(&self) -> &dyn ConnectionIdDecoder;
}
/// An `RandomConnectionIdGenerator` produces connection IDs of /// a fixed length and random content. No effort is made to /// prevent collisions. pubstruct RandomConnectionIdGenerator {
len: usize,
}
/// A single connection ID, as saved from `NEW_CONNECTION_ID`. /// This is templated so that the connection ID entries from a peer can be /// saved with a stateless reset token. Local entries don't need that. #[derive(Debug, PartialEq, Eq, Clone)] pubstruct ConnectionIdEntry<SRT: Clone + PartialEq> { /// The sequence number.
seqno: u64, /// The connection ID.
cid: ConnectionId, /// The corresponding stateless reset token.
srt: SRT,
}
impl ConnectionIdEntry<Srt> { /// Create the first entry, which won't have a stateless reset token. pubfn initial_remote(cid: ConnectionId) -> Self { Self::new(Self::SEQNO_INITIAL, cid, Srt::random())
}
/// Create an empty for when the peer chooses empty connection IDs. /// This uses a special sequence number just because it can. pubfn empty_remote() -> Self { Self::new(
ConnectionIdManager::SEQNO_EMPTY,
ConnectionId::from(&[]),
Srt::random(),
)
}
/// Determine whether this is a valid stateless reset. pubfn is_stateless_reset(&self, token: &Srt) -> bool { // A sequence number of 2^62 or more has no corresponding stateless reset token.
(self.seqno < (1 << 62)) && self.srt.eq(token)
}
/// Return true if the two contain any equal parts. fn any_part_equal(&self, other: &Self) -> bool { self.seqno == other.seqno || self.cid == other.cid || self.srt == other.srt
}
/// The sequence number of this entry. pubconstfn sequence_number(&self) -> u64 { self.seqno
}
/// Write the entry out in a `NEW_CONNECTION_ID` frame. /// Returns `true` if the frame was written, `false` if there is insufficient space. pubfn write<B: Buffer>(
&self,
builder: &mut packet::Builder<B>,
stats: &mut FrameStats,
) -> bool { let len = 1 + Encoder::varint_len(self.seqno) + 1 + 1 + self.cid.len() + Srt::LEN; if builder.remaining() < len { returnfalse;
}
/// Update the stateless reset token. This panics if the sequence number is non-zero. pubfn set_stateless_reset_token(&mutself, srt: SRT) {
assert_eq!(self.seqno, Self::SEQNO_INITIAL); self.srt = srt;
}
/// Replace the connection ID. This panics if the sequence number is non-zero. pubfn update_cid(&mutself, cid: ConnectionId) {
assert_eq!(self.seqno, Self::SEQNO_INITIAL); self.cid = cid;
}
/// A collection of connection IDs that are indexed by a sequence number. /// Used to store connection IDs that are provided by a peer. #[derive(Debug, Default)] pubstruct ConnectionIdStore<SRT: Clone + PartialEq> {
cids: SmallVec<[ConnectionIdEntry<SRT>; 8]>,
}
impl ConnectionIdStore<Srt> { pubfn add_remote(&mutself, entry: ConnectionIdEntry<Srt>) -> Res<()> { // It's OK if this perfectly matches an existing entry. ifself.cids.iter().any(|c| c == &entry) { return Ok(());
} // It's not OK if any individual piece matches though. ifself.cids.iter().any(|c| c.any_part_equal(&entry)) {
qinfo!("ConnectionIdStore found reused part in NEW_CONNECTION_ID"); return Err(Error::ProtocolViolation);
}
// Insert in order so that we use them in order where possible. iflet Err(idx) = self.cids.binary_search_by_key(&entry.seqno, |e| e.seqno) { self.cids.insert(idx, entry);
Ok(())
} else {
Err(Error::ProtocolViolation)
}
}
// Retire connection IDs and return the sequence numbers of those that were retired. pubfn retire_prior_to(&mutself, retire_prior: u64) -> Vec<u64> { letmut retired = Vec::new(); self.cids.retain(|e| { if !e.is_empty() && e.seqno < retire_prior {
retired.push(e.seqno); false
} else { true
}
});
retired
}
}
// Ideally this would be an implementation of `Deref`, but it doesn't // seem to be possible to convince the compiler to build anything useful. impl<'a: 'b, 'b> ConnectionIdDecoderRef<'a> { pubfn as_ref(&'a self) -> &'b dyn ConnectionIdDecoder { self.generator.as_decoder()
}
}
/// A connection ID manager looks after the generation of connection IDs, /// the set of connection IDs that are valid for the connection, and the /// generation of `NEW_CONNECTION_ID` frames. pubstruct ConnectionIdManager { /// The `ConnectionIdGenerator` instance that is used to create connection IDs.
generator: Rc<RefCell<dyn ConnectionIdGenerator>>, /// The connection IDs that we will accept. /// This includes any we advertise in `NEW_CONNECTION_ID` that haven't been bound to a path /// yet. During the handshake at the server, it also includes the randomized DCID pick by /// the client.
connection_ids: ConnectionIdStore<()>, /// The maximum number of connection IDs this will accept. This is at least 2 and won't /// be more than `Self::ACTIVE_LIMIT`.
limit: usize, /// The next sequence number that will be used for sending `NEW_CONNECTION_ID` frames.
next_seqno: u64, /// Outstanding, but lost `NEW_CONNECTION_ID` frames will be stored here.
lost_new_connection_id: Vec<ConnectionIdEntry<Srt>>,
}
/// A special value. See `ConnectionIdManager::add_odcid`. const SEQNO_ODCID: u64 = u64::MAX;
/// A special value. See `ConnectionIdEntry::empty_remote`. const SEQNO_EMPTY: u64 = u64::MAX - 1;
pubconst SEQNO_PREFERRED: u64 = 1;
pubfn new(generator: Rc<RefCell<dyn ConnectionIdGenerator>>, initial: ConnectionId) -> Self { letmut connection_ids = ConnectionIdStore::default();
connection_ids.add_local(ConnectionIdEntry::initial_local(initial)); Self {
generator,
connection_ids, // A note about initializing the limit to 2. // For a server, the number of connection IDs that are tracked at the point that // it is first possible to send `NEW_CONNECTION_ID` is 2. One is the client-generated // destination connection (stored with a sequence number of `HANDSHAKE_SEQNO`); the // other being the handshake value (seqno 0). As a result, `NEW_CONNECTION_ID` // won't be sent until after the handshake completes, because this initial // value remains until the connection completes and transport parameters are handled.
limit: 2,
next_seqno: 1,
lost_new_connection_id: Vec::new(),
}
}
pubfn retire(&mutself, seqno: u64) { // TODO(mt) - consider keeping connection IDs around for a short while.
let empty_cid = seqno == Self::SEQNO_EMPTY
|| self
.connection_ids
.cids
.iter()
.any(|c| c.seqno == seqno && c.cid.is_empty()); if empty_cid {
qdebug!("Connection ID {seqno} is zero-length, not retiring");
} else { self.connection_ids.retire(seqno); self.lost_new_connection_id.retain(|cid| cid.seqno != seqno);
}
}
/// During the handshake, a server needs to regard the client's choice of destination /// connection ID as valid. This function saves it in the store in a special place. /// Note that this is only done *after* an Initial packet from the client is /// successfully processed. pubfn add_odcid(&mutself, cid: ConnectionId) { let entry = ConnectionIdEntry::new(Self::SEQNO_ODCID, cid, ()); self.connection_ids.add_local(entry);
}
/// Stop treating the original destination connection ID as valid. pubfn remove_odcid(&mutself) { self.connection_ids.retire(Self::SEQNO_ODCID);
}
// Keep writing while we have fewer than the limit of active connection IDs // and while there is room for more. This uses the longest connection ID // length to simplify (assuming Retire Prior To is just 1 byte). whileself.connection_ids.len() < self.limit && builder.remaining() >= 47 { let maybe_cid = self.generator.borrow_mut().generate_cid(); iflet Some(cid) = maybe_cid {
assert_ne!(cid.len(), 0); let seqno = self.next_seqno; self.next_seqno += 1; self.connection_ids
.add_local(ConnectionIdEntry::new(seqno, cid.clone(), ()));
// TODO: generate the stateless reset tokens from the connection ID and a key. let entry = ConnectionIdEntry::new(seqno, cid, Srt::random());
entry.write(builder, stats);
tokens.push(recovery::Token::NewConnectionId(entry));
}
}
}
/// A write with exactly the right remaining space must succeed. #[test] fn write_succeeds_with_exact_remaining() {
fixture_init(); let entry = ConnectionIdEntry::new(1, ConnectionId::from(&[0xab]), Srt::random()); let len = new_connection_id_frame_len(&entry); // Capacity = len + 1 so that Builder::short (which consumes 1 byte) leaves exactly `len`. let enc = Encoder::with_capacity(len + 1); letmut builder = packet::Builder::short(enc, false, Some(&[]), len + 1);
assert_eq!(builder.remaining(), len, "exactly `len` bytes remaining");
assert!(
entry.write(&mut builder, &mut FrameStats::default()), "write must succeed when remaining == len"
);
}
#[test] fn write_checks_length_correctly() {
fixture_init(); let entry = ConnectionIdEntry::new(1, ConnectionId::from(&[]), Srt::random()); let limit = new_connection_id_frame_len(&entry); let enc = Encoder::with_capacity(limit); letmut builder = packet::Builder::short(enc, false, Some(&[]), limit);
assert_eq!(
builder.remaining(),
limit - 1, "Builder::short consumed one byte"
);
assert!(
!entry.write(&mut builder, &mut FrameStats::default()), "couldn't write frame into too-short builder",
);
}
#[test] fn connection_id_ref_debug_format() { let bytes = [0xde, 0xad]; let cid_ref = crate::cid::ConnectionIdRef::from(&bytes[..]);
assert_eq!(format!("{cid_ref:?}"), "CID [2]: dead");
}
#[test] fn empty_connection_id_generator() { usecrate::cid::{ConnectionIdGenerator as _, EmptyConnectionIdGenerator}; letmut g = EmptyConnectionIdGenerator::default();
assert!(g.generates_empty_cids()); let cid = g.generate_cid().expect("generates Some");
assert!(cid.is_empty());
}
#[test] fn is_stateless_reset_seqno_boundary() {
fixture_init(); let srt = Srt::random(); // Sequence number < 2^62 should match SRT. let entry = ConnectionIdEntry::new((1 << 62) - 1, ConnectionId::from(&[1]), srt.clone());
assert!(entry.is_stateless_reset(&srt)); // Sequence number >= 2^62 has no valid SRT (should return false). let entry_high = ConnectionIdEntry::new(1 << 62, ConnectionId::from(&[1]), srt.clone());
assert!(!entry_high.is_stateless_reset(&srt));
}
#[test] fn connection_id_entry_is_empty() {
fixture_init(); let srt = Srt::random(); // SEQNO_EMPTY makes it empty. let empty_seqno = ConnectionIdEntry::new(
ConnectionIdManager::SEQNO_EMPTY,
ConnectionId::from(&[1]),
srt.clone(),
);
assert!(empty_seqno.is_empty()); // Empty CID also makes it empty. let empty_cid = ConnectionIdEntry::new(42, ConnectionId::from(&[]), srt.clone());
assert!(empty_cid.is_empty()); // Non-empty seqno and CID is not empty. let non_empty = ConnectionIdEntry::new(1, ConnectionId::from(&[1]), srt);
assert!(!non_empty.is_empty());
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.