From 7c0bf4807c757ab4a721cc771283a28a9949511f Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Tue, 9 Jun 2026 11:53:25 +0300 Subject: [PATCH 1/2] Switch from Arc to Option> to make it visible if the mechanism (client side or server side token store/log) is disabled. We don't remove the Noop impls yet since that would break the public API. --- noq-proto/src/config/mod.rs | 16 +++++++--------- noq-proto/src/connection/mod.rs | 13 +++++++++---- noq-proto/src/token.rs | 7 ++++--- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/noq-proto/src/config/mod.rs b/noq-proto/src/config/mod.rs index 32b944223..19e5eff7b 100644 --- a/noq-proto/src/config/mod.rs +++ b/noq-proto/src/config/mod.rs @@ -13,8 +13,6 @@ use thiserror::Error; #[cfg(feature = "bloom")] use crate::BloomTokenLog; -#[cfg(not(feature = "bloom"))] -use crate::NoneTokenLog; #[cfg(all(feature = "rustls", any(feature = "aws-lc-rs", feature = "ring")))] use crate::crypto::rustls::{QuicServerConfig, configured_provider}; use crate::{ @@ -475,7 +473,7 @@ impl fmt::Debug for ServerConfig { #[derive(Clone)] pub struct ValidationTokenConfig { pub(crate) lifetime: Duration, - pub(crate) log: Arc, + pub(crate) log: Option>, pub(crate) sent: u32, } @@ -500,7 +498,7 @@ impl ValidationTokenConfig { /// which makes the server ignore all address validation tokens (that is, tokens originating /// from NEW_TOKEN frames--retry tokens are not affected). pub fn log(&mut self, log: Arc) -> &mut Self { - self.log = log; + self.log = Some(log); self } @@ -519,9 +517,9 @@ impl ValidationTokenConfig { impl Default for ValidationTokenConfig { fn default() -> Self { #[cfg(feature = "bloom")] - let log = Arc::new(BloomTokenLog::default()); + let log = Some(Arc::new(BloomTokenLog::default()) as Arc); #[cfg(not(feature = "bloom"))] - let log = Arc::new(NoneTokenLog); + let log = None; Self { lifetime: Duration::from_secs(2 * 7 * 24 * 60 * 60), log, @@ -553,7 +551,7 @@ pub struct ClientConfig { pub(crate) crypto: Arc, /// Validation token store to use - pub(crate) token_store: Arc, + pub(crate) token_store: Option>, /// Provider that populates the destination connection ID of Initial Packets pub(crate) initial_dst_cid_provider: Arc ConnectionId + Send + Sync>, @@ -568,7 +566,7 @@ impl ClientConfig { Self { transport: Default::default(), crypto, - token_store: Arc::new(TokenMemoryCache::default()), + token_store: Some(Arc::new(TokenMemoryCache::default())), initial_dst_cid_provider: Arc::new(|| { RandomConnectionIdGenerator::new(MAX_CID_SIZE).generate_cid() }), @@ -602,7 +600,7 @@ impl ClientConfig { /// /// Defaults to [`TokenMemoryCache`], which is suitable for most internet applications. pub fn token_store(&mut self, store: Arc) -> &mut Self { - self.token_store = store; + self.token_store = Some(store); self } diff --git a/noq-proto/src/connection/mod.rs b/noq-proto/src/connection/mod.rs index 661aff8c4..aca44d79a 100644 --- a/noq-proto/src/connection/mod.rs +++ b/noq-proto/src/connection/mod.rs @@ -5269,7 +5269,9 @@ impl Connection { return Err(TransportError::FRAME_ENCODING_ERROR("empty token")); } trace!("got new token"); - token_store.insert(server_name, token); + if let Some(token_store) = token_store { + token_store.insert(server_name, token); + } } Frame::Datagram(datagram) => { if self @@ -7347,7 +7349,7 @@ enum ConnectionSide { Client { /// Sent in every outgoing Initial packet. Always empty after Initial keys are discarded token: Bytes, - token_store: Arc, + token_store: Option>, server_name: String, }, Server { @@ -7379,7 +7381,10 @@ impl From for ConnectionSide { token_store, server_name, } => Self::Client { - token: token_store.take(&server_name).unwrap_or_default(), + token: token_store + .as_ref() + .and_then(|token_store| token_store.take(&server_name)) + .unwrap_or_default(), token_store, server_name, }, @@ -7395,7 +7400,7 @@ impl From for ConnectionSide { /// Parameters to `Connection::new` specific to it being client-side or server-side pub(crate) enum SideArgs { Client { - token_store: Arc, + token_store: Option>, server_name: String, }, Server { diff --git a/noq-proto/src/token.rs b/noq-proto/src/token.rs index 8de2b5d6f..2f84adf0b 100644 --- a/noq-proto/src/token.rs +++ b/noq-proto/src/token.rs @@ -170,9 +170,10 @@ impl IncomingToken { { return Ok(unvalidated); } - if server_config - .validation_token - .log + let Some(log) = &server_config.validation_token.log else { + return Ok(unvalidated); + }; + if log .check_and_insert(retry.nonce, issued, server_config.validation_token.lifetime) .is_err() { From c76925f826bf87520814f7b159ee1314afbcc16a Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Tue, 9 Jun 2026 12:32:35 +0300 Subject: [PATCH 2/2] Add ways to disable both the client side token store and the server side token log This is unidiomatic for the library. In other places we have setters on builders that take an option. But it's the only thing we can do that does not break the public API. E.g. pub fn mtu_discovery_config(&mut self, value: Option) -> &mut Self { --- noq-proto/src/config/mod.rs | 12 ++++++++++++ noq-proto/src/lib.rs | 4 +++- noq-proto/src/token.rs | 4 ++++ noq/src/lib.rs | 11 ++++++----- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/noq-proto/src/config/mod.rs b/noq-proto/src/config/mod.rs index 19e5eff7b..6c1bacc8d 100644 --- a/noq-proto/src/config/mod.rs +++ b/noq-proto/src/config/mod.rs @@ -502,6 +502,12 @@ impl ValidationTokenConfig { self } + /// Disable the [`TokenLog`], making the server ignore all address validation tokens + pub fn disable_token_log(&mut self) -> &mut Self { + self.log = None; + self + } + /// Number of address validation tokens sent to a client when its path is validated /// /// This refers only to tokens sent in NEW_TOKEN frames, in contrast to retry tokens. @@ -604,6 +610,12 @@ impl ClientConfig { self } + /// Disable the [`TokenStore`], so that no validation tokens are stored + pub fn disable_token_store(&mut self) -> &mut Self { + self.token_store = None; + self + } + /// Set the QUIC version to use pub fn version(&mut self, version: u32) -> &mut Self { self.version = version; diff --git a/noq-proto/src/lib.rs b/noq-proto/src/lib.rs index 85bfdcf9e..1a81886e4 100644 --- a/noq-proto/src/lib.rs +++ b/noq-proto/src/lib.rs @@ -101,7 +101,9 @@ pub use crate::cid_generator::{ mod token; use token::ResetToken; -pub use token::{NoneTokenLog, NoneTokenStore, TokenLog, TokenReuseError, TokenStore}; +#[allow(deprecated)] +pub use token::{NoneTokenLog, NoneTokenStore}; +pub use token::{TokenLog, TokenReuseError, TokenStore}; mod address_discovery; diff --git a/noq-proto/src/token.rs b/noq-proto/src/token.rs index 2f84adf0b..dbb30da0a 100644 --- a/noq-proto/src/token.rs +++ b/noq-proto/src/token.rs @@ -66,8 +66,10 @@ pub trait TokenLog: Send + Sync { pub struct TokenReuseError; /// Null implementation of [`TokenLog`], which never accepts tokens +#[deprecated(note = "use `ValidationTokenConfig::disable_token_log` instead")] pub struct NoneTokenLog; +#[allow(deprecated)] impl TokenLog for NoneTokenLog { fn check_and_insert(&self, _: u128, _: SystemTime, _: Duration) -> Result<(), TokenReuseError> { Err(TokenReuseError) @@ -92,8 +94,10 @@ pub trait TokenStore: Send + Sync { } /// Null implementation of [`TokenStore`], which does not store any tokens +#[deprecated(note = "use `ClientConfig::disable_token_store` instead")] pub struct NoneTokenStore; +#[allow(deprecated)] impl TokenStore for NoneTokenStore { fn insert(&self, _: &str, _: Bytes) {} fn take(&self, _: &str) -> Option { diff --git a/noq/src/lib.rs b/noq/src/lib.rs index ac43d2ee6..b9eb063a7 100644 --- a/noq/src/lib.rs +++ b/noq/src/lib.rs @@ -65,12 +65,13 @@ pub use proto::{ ClosedStream, ConfigError, ConnectError, ConnectionClose, ConnectionError, ConnectionId, ConnectionIdGenerator, ConnectionStats, DecryptedInitial, Dir, EcnCodepoint, EndpointConfig, FourTuple, FrameStats, FrameType, IdleTimeout, InvalidCid, MtuDiscoveryConfig, - NetworkChangeHint, NoneTokenLog, NoneTokenStore, PathError, PathEvent, PathId, PathStats, - PathStatus, ServerConfig, SetPathStatusError, Side, StdSystemTime, StreamId, TimeSource, - TokenLog, TokenMemoryCache, TokenReuseError, TokenStore, Transmit, TransportConfig, - TransportErrorCode, UdpStats, ValidationTokenConfig, VarInt, VarIntBoundsExceeded, congestion, - crypto, + NetworkChangeHint, PathError, PathEvent, PathId, PathStats, PathStatus, ServerConfig, + SetPathStatusError, Side, StdSystemTime, StreamId, TimeSource, TokenLog, TokenMemoryCache, + TokenReuseError, TokenStore, Transmit, TransportConfig, TransportErrorCode, UdpStats, + ValidationTokenConfig, VarInt, VarIntBoundsExceeded, congestion, crypto, }; +#[allow(deprecated)] +pub use proto::{NoneTokenLog, NoneTokenStore}; #[cfg(feature = "qlog")] pub use proto::{QlogConfig, QlogFactory, QlogFileFactory}; #[cfg(feature = "rustls")]