Skip to content

Commit

Permalink
Support storing ApplicationData in Entry
Browse files Browse the repository at this point in the history
This stores arbitrary metadata (Arc<dyn Any>) in each Entry. By default,
we share a single `Arc<()>` across all entries, but applications can
register a callback to produce arbitrary other values.
  • Loading branch information
Mark-Simulacrum committed Mar 7, 2025
1 parent a9e7673 commit 465fd1b
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 6 deletions.
15 changes: 14 additions & 1 deletion dc/s2n-quic-dc/src/path/secret/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::{
path::secret::{open, seal, stateless_reset},
stream::TransportFeatures,
};
pub use entry::ApplicationData;
use s2n_quic_core::{dc, time};
use std::{net::SocketAddr, sync::Arc};

Expand All @@ -26,7 +27,7 @@ pub mod testing;
#[cfg(test)]
mod event_tests;

use entry::Entry;
pub use entry::Entry;
use store::Store;

pub use entry::{ApplicationPair, Bidirectional, ControlPair};
Expand Down Expand Up @@ -202,6 +203,7 @@ impl Map {
receiver::State::new(),
dc::testing::TEST_APPLICATION_PARAMS,
dc::testing::TEST_REHANDSHAKE_PERIOD,
Arc::new(()),
);
let entry = Arc::new(entry);
provider.store.test_insert(entry);
Expand Down Expand Up @@ -255,6 +257,7 @@ impl Map {
super::receiver::State::new(),
dc::testing::TEST_APPLICATION_PARAMS,
dc::testing::TEST_REHANDSHAKE_PERIOD,
Arc::new(()),
);
let entry = Arc::new(entry);
map.store.test_insert(entry);
Expand All @@ -269,4 +272,14 @@ impl Map {

client_id
}

#[allow(clippy::type_complexity)]
pub fn register_make_application_data(
&self,
cb: Box<
dyn Fn(&dyn s2n_quic_core::crypto::tls::TlsSession) -> ApplicationData + Send + Sync,
>,
) {
self.store.register_make_application_data(cb);
}
}
21 changes: 20 additions & 1 deletion dc/s2n-quic-dc/src/path/secret/map/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use rand::Rng as _;
use s2n_codec::EncoderBuffer;
use s2n_quic_core::{dc, varint::VarInt};
use std::{
any::Any,
net::SocketAddr,
sync::{
atomic::{AtomicU32, AtomicU8, Ordering},
Expand All @@ -31,8 +32,10 @@ use std::{
#[cfg(test)]
mod tests;

pub type ApplicationData = Arc<dyn Any + Send + Sync>;

#[derive(Debug)]
pub(super) struct Entry {
pub struct Entry {
creation_time: Instant,
rehandshake_delta_secs: AtomicU32,
peer: SocketAddr,
Expand All @@ -44,6 +47,7 @@ pub(super) struct Entry {
// we store this as a u8 to allow the cleaner to separately "take" accessed for id and addr
// maps while not having two writes and wasting an extra byte of space.
accessed: AtomicU8,
application_data: ApplicationData,
}

impl SizeOf for Entry {
Expand All @@ -58,6 +62,7 @@ impl SizeOf for Entry {
receiver,
parameters,
accessed,
application_data,
} = self;
creation_time.size()
+ rehandshake_delta_secs.size()
Expand All @@ -68,6 +73,13 @@ impl SizeOf for Entry {
+ receiver.size()
+ parameters.size()
+ accessed.size()
+ application_data.size()
}
}

impl SizeOf for ApplicationData {
fn size(&self) -> usize {
std::mem::size_of_val(self)
}
}

Expand All @@ -82,6 +94,7 @@ impl Entry {
receiver: receiver::State,
parameters: dc::ApplicationParams,
rehandshake_time: Duration,
application_data: ApplicationData,
) -> Self {
// clamp max datagram size to a well-known value
parameters
Expand All @@ -99,6 +112,7 @@ impl Entry {
receiver,
parameters,
accessed: AtomicU8::new(0),
application_data,
};
entry.rehandshake_time_reschedule(rehandshake_time);
entry
Expand All @@ -123,6 +137,7 @@ impl Entry {
receiver,
dc::testing::TEST_APPLICATION_PARAMS,
dc::testing::TEST_REHANDSHAKE_PERIOD,
Arc::new(()),
))
}

Expand Down Expand Up @@ -287,6 +302,10 @@ impl Entry {
pub fn control_sealer(&self) -> crate::crypto::awslc::seal::control::Secret {
self.secret.control_sealer()
}

pub fn application_data(&self) -> &ApplicationData {
&self.application_data
}
}

impl receiver::Error {
Expand Down
2 changes: 1 addition & 1 deletion dc/s2n-quic-dc/src/path/secret/map/entry/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fn entry_size() {
if should_check {
assert_eq!(
Entry::fake((std::net::Ipv4Addr::LOCALHOST, 0).into(), None).size(),
295
311
);
}
}
7 changes: 6 additions & 1 deletion dc/s2n-quic-dc/src/path/secret/map/handshake.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use super::{Entry, Map};
use super::{entry::ApplicationData, Entry, Map};
use crate::{
packet::secret_control as control,
path::secret::{receiver, schedule, sender},
Expand All @@ -24,6 +24,7 @@ pub struct HandshakingPath {
endpoint_type: s2n_quic_core::endpoint::Type,
secret: Option<schedule::Secret>,
entry: Option<Arc<Entry>>,
application_data: Option<ApplicationData>,
map: Map,
}

Expand All @@ -41,6 +42,7 @@ impl HandshakingPath {
endpoint_type,
secret: None,
entry: None,
application_data: None,
map,
}
}
Expand Down Expand Up @@ -82,6 +84,8 @@ impl dc::Path for HandshakingPath {
&mut self,
session: &impl s2n_quic_core::crypto::tls::TlsSession,
) -> Result<Vec<s2n_quic_core::stateless_reset::Token>, s2n_quic_core::transport::Error> {
self.application_data = Some(self.map.application_data.application_data(session));

let mut material = Zeroizing::new([0; TLS_EXPORTER_LENGTH]);
session
.tls_exporter(
Expand Down Expand Up @@ -134,6 +138,7 @@ impl dc::Path for HandshakingPath {
receiver,
self.parameters.clone(),
self.map.store.rehandshake_period(),
self.application_data.take().unwrap(),
);
let entry = Arc::new(entry);
self.entry = Some(entry.clone());
Expand Down
46 changes: 45 additions & 1 deletion dc/s2n-quic-dc/src/path/secret/map/state.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use super::{cleaner::Cleaner, stateless_reset, Entry, Store};
use super::{cleaner::Cleaner, entry::ApplicationData, stateless_reset, Entry, Store};
use crate::{
credentials::{Credentials, Id},
crypto,
Expand Down Expand Up @@ -211,6 +211,19 @@ where
clock: C,

subscriber: S,

#[allow(clippy::type_complexity)]
mk_application_data: RwLock<
Option<
Box<
dyn Fn(&dyn s2n_quic_core::crypto::tls::TlsSession) -> ApplicationData
+ Send
+ Sync,
>,
>,
>,

dummy_application_data: ApplicationData,
}

// Share control sockets -- we only send on these so it doesn't really matter if there's only one
Expand Down Expand Up @@ -266,6 +279,8 @@ where
clock,
subscriber,
request_handshake: RwLock::new(None),
mk_application_data: RwLock::new(None),
dummy_application_data: Arc::new(()),
};

// Growing to double our maximum inserted entries should ensure that we never grow again, see:
Expand Down Expand Up @@ -645,6 +660,20 @@ where
self.register_request_handshake(cb);
}

#[allow(clippy::type_complexity)]
fn register_make_application_data(
&self,
cb: Box<
dyn Fn(&dyn s2n_quic_core::crypto::tls::TlsSession) -> ApplicationData + Send + Sync,
>,
) {
// FIXME: Maybe panic if already initialized?
*self
.mk_application_data
.write()
.unwrap_or_else(|e| e.into_inner()) = Some(cb);
}

fn get_by_addr_untracked(&self, peer: &SocketAddr) -> Option<Arc<Entry>> {
self.peers.get(*peer)
}
Expand Down Expand Up @@ -845,6 +874,21 @@ where
fn test_stop_cleaner(&self) {
self.cleaner.stop();
}

fn application_data(
&self,
session: &dyn s2n_quic_core::crypto::tls::TlsSession,
) -> ApplicationData {
if let Some(ctxt) = &*self
.mk_application_data
.read()
.unwrap_or_else(|e| e.into_inner())
{
(ctxt)(session)
} else {
self.dummy_application_data.clone()
}
}
}

impl<C, S> Drop for State<C, S>
Expand Down
1 change: 1 addition & 0 deletions dc/s2n-quic-dc/src/path/secret/map/state/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ impl Model {
receiver::State::new(),
dc::testing::TEST_APPLICATION_PARAMS,
dc::testing::TEST_REHANDSHAKE_PERIOD,
Arc::new(()),
)));

self.invariants.insert(Invariant::ContainsIp(ip));
Expand Down
15 changes: 14 additions & 1 deletion dc/s2n-quic-dc/src/path/secret/map/store.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use super::Entry;
use super::{entry::ApplicationData, Entry};
use crate::{
credentials::{Credentials, Id},
packet::{secret_control as control, Packet, WireVersion},
Expand Down Expand Up @@ -101,4 +101,17 @@ pub trait Store: 'static + Send + Sync {

Some(state.clone())
}

#[allow(clippy::type_complexity)]
fn register_make_application_data(
&self,
cb: Box<
dyn Fn(&dyn s2n_quic_core::crypto::tls::TlsSession) -> ApplicationData + Send + Sync,
>,
);

fn application_data(
&self,
session: &dyn s2n_quic_core::crypto::tls::TlsSession,
) -> ApplicationData;
}

0 comments on commit 465fd1b

Please sign in to comment.