Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to rustls@0.23 and quinn@0.11 #200

Merged
merged 1 commit into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions wtransport/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ required-features = ["self-signed"]
[dependencies]
bytes = "1.4.0"
pem = "3.0.4"
quinn = "0.10.1"
quinn = { version = "0.11.3", default-features = false, features = ["runtime-tokio", "rustls"] }
finnbear marked this conversation as resolved.
Show resolved Hide resolved
rcgen = { version = "0.13.1", optional = true }
rustls = { version = "0.21.1", features = ["dangerous_configuration"] }
rustls = { version = "0.23.12", default-features = false, features = ["ring"] }
finnbear marked this conversation as resolved.
Show resolved Hide resolved
rustls-native-certs = "0.7.1"
rustls-pemfile = "2.1.1"
rustls-pki-types = "1.3.1"
rustls-pemfile = "2.1.3"
rustls-pki-types = "1.8.0"
sha2 = "0.10.8"
socket2 = "0.5.3"
thiserror = "1.0.40"
Expand All @@ -58,6 +58,7 @@ tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
default = ["self-signed"]
dangerous-configuration = []
quinn = []
quinn-log = ["quinn/log"]
self-signed = ["dep:rcgen"]

[package.metadata.docs.rs]
Expand All @@ -77,7 +78,7 @@ allowed_external_types = [
"quinn_proto::config::TransportConfig",
"quinn_proto::connection::ConnectionError",
"rustls",
"rustls::anchors::RootCertStore",
"rustls::webpki::anchors::RootCertStore",
"rustls::client::client_conn::ClientConfig",
"rustls::server::server_conn::ServerConfig",
"rustls::verify::ServerCertVerifier",
Expand Down
36 changes: 29 additions & 7 deletions wtransport/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,10 @@ impl ServerConfigBuilder<states::WantsIdentity> {
}

/// Allows for manual configuration of a custom TLS setup using a provided
/// [`rustls::ServerConfig`].
/// [`rustls::ServerConfig`], which must support
/// [`rustls::CipherSuite::TLS13_AES_128_GCM_SHA256`]. A suitable configuration
/// can be obtained using the `ring` crypto provider with a set of versions containing
/// [`rustls::version::TLS13`].
///
/// This method is provided for advanced users who need fine-grained control over the
/// TLS configuration. It allows you to pass a preconfigured [`rustls::ServerConfig`]
Expand All @@ -376,7 +379,6 @@ impl ServerConfigBuilder<states::WantsIdentity> {
/// let custom_tls_config = rustls::ServerConfig::builder();
/// // Customize TLS settings here...
/// # let custom_tls_config = custom_tls_config
/// # .with_safe_defaults()
/// # .with_no_client_auth()
/// # .with_single_cert(todo!(), todo!()).unwrap();
///
Expand Down Expand Up @@ -456,7 +458,8 @@ impl ServerConfigBuilder<states::WantsIdentity> {
/// # Parameters
///
/// - `tls_config`: A custom [`TlsServerConfig`] instance that allows you to specify
/// detailed TLS settings, such as ciphersuites, certificate verification, and more.
/// detailed TLS settings, such as ciphersuites, certificate verification, and more. It must
/// support TLS 1.3 (see the documentation of [`Self::with_custom_tls`]).
/// - `quic_transport_config`: A custom [`QuicTransportConfig`] instance that allows you to specify
/// various QUIC transport-layer settings according to your requirements.
#[cfg(feature = "quinn")]
Expand Down Expand Up @@ -486,9 +489,18 @@ impl ServerConfigBuilder<states::WantsIdentity> {

impl ServerConfigBuilder<states::WantsTransportConfigServer> {
/// Completes configuration process.
///
/// # Panics
///
/// See the documentation of [`Self::with_custom_tls`] for the TLS 1.3 requirement.
#[must_use]
pub fn build(self) -> ServerConfig {
let mut quic_config = QuicServerConfig::with_crypto(Arc::new(self.0.tls_config));
let crypto: Arc<quinn::crypto::rustls::QuicServerConfig> = Arc::new(
quinn::crypto::rustls::QuicServerConfig::try_from(self.0.tls_config)
.expect("CipherSuite::TLS13_AES_128_GCM_SHA256 missing"),
);
let mut quic_config = QuicServerConfig::with_crypto(crypto);

quic_config.transport_config(Arc::new(self.0.transport_config));
quic_config.migration(self.0.migration);

Expand Down Expand Up @@ -851,7 +863,10 @@ impl ClientConfigBuilder<states::WantsRootStore> {
}

/// Allows for manual configuration of a custom TLS setup using a provided
/// [`rustls::ClientConfig`].
/// [`rustls::ClientConfig`], which must support
/// [`rustls::CipherSuite::TLS13_AES_128_GCM_SHA256`]. A suitable configuration
/// can be obtained using the `ring` crypto provider with a set of versions containing
/// [`rustls::version::TLS13`].
///
/// This method is provided for advanced users who need fine-grained control over the
/// TLS configuration. It allows you to pass a preconfigured [`rustls::ClientConfig`]
Expand Down Expand Up @@ -915,7 +930,8 @@ impl ClientConfigBuilder<states::WantsRootStore> {
/// # Parameters
///
/// - `tls_config`: A custom [`TlsClientConfig`] instance that allows you to specify
/// detailed TLS settings, such as ciphersuites, certificate verification, and more.
/// detailed TLS settings, such as ciphersuites, certificate verification, and more. It must
/// support TLS 1.3 (see the documentation of [`Self::with_custom_tls`]).
/// - `quic_transport_config`: A custom [`QuicTransportConfig`] instance that allows you to specify
/// various QUIC transport-layer settings according to your requirements.
#[cfg(feature = "quinn")]
Expand Down Expand Up @@ -945,9 +961,15 @@ impl ClientConfigBuilder<states::WantsRootStore> {

impl ClientConfigBuilder<states::WantsTransportConfigClient> {
/// Completes configuration process.
///
/// # Panics
///
/// See the documentation of [`Self::with_custom_tls`] for the TLS 1.3 requirement.
#[must_use]
pub fn build(self) -> ClientConfig {
let mut quic_config = QuicClientConfig::new(Arc::new(self.0.tls_config));
let crypto = quinn::crypto::rustls::QuicClientConfig::try_from(self.0.tls_config)
.expect("CipherSuite::TLS13_AES_128_GCM_SHA256 missing");
let mut quic_config = QuicClientConfig::new(Arc::new(crypto));
quic_config.transport_config(Arc::new(self.0.transport_config));

ClientConfig {
Expand Down
4 changes: 2 additions & 2 deletions wtransport/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,10 +382,10 @@ impl Connection {
/// is not available, `None` is returned.
pub fn peer_identity(&self) -> Option<CertificateChain> {
self.quic_connection.peer_identity().map(|any| {
any.downcast::<Vec<rustls::Certificate>>()
any.downcast::<Vec<rustls_pki_types::CertificateDer<'static>>>()
.expect("rustls certificate vector")
.into_iter()
.map(|c| Certificate::from_der(c.0).expect("valid certificate"))
.map(Certificate::from_rustls_pki)
.collect()
})
}
Expand Down
36 changes: 24 additions & 12 deletions wtransport/src/driver/streams/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,13 @@ impl QuicSendStream {

#[inline(always)]
pub async fn finish(&mut self) -> Result<(), StreamWriteError> {
self.0.finish().await?;
Ok(())
let _ = self.0.finish();
let result = self.stopped().await;
if matches!(result, StreamWriteError::Closed) {
Ok(())
} else {
Err(result)
}
}

#[inline(always)]
Expand All @@ -53,24 +58,27 @@ impl QuicSendStream {
}

#[inline(always)]
/// # Panics
///
/// If `reset` was called.
pub fn priority(&self) -> i32 {
self.0.priority().expect("Stream has been reset")
}

pub async fn stopped(&mut self) -> StreamWriteError {
match self.0.stopped().await {
Ok(code) => StreamWriteError::Stopped(varint_q2w(code)),
Ok(None) => StreamWriteError::Closed,
Ok(Some(code)) => StreamWriteError::Stopped(varint_q2w(code)),
Err(quinn::StoppedError::ConnectionLost(_)) => StreamWriteError::NotConnected,
Err(quinn::StoppedError::UnknownStream) => StreamWriteError::QuicProto,
Err(quinn::StoppedError::ZeroRttRejected) => StreamWriteError::QuicProto,
}
}

#[inline(always)]
pub fn reset(mut self, error_code: VarInt) {
pub fn reset(&mut self, error_code: VarInt) -> Result<(), StreamWriteError> {
self.0
.reset(varint_w2q(error_code))
.expect("Stream has been already reset");
.map_err(|_| StreamWriteError::Closed)
}

#[inline(always)]
Expand Down Expand Up @@ -160,7 +168,9 @@ impl QuicRecvStream {
.read_exact(buf)
.await
.map_err(|quic_error| match quic_error {
quinn::ReadExactError::FinishedEarly => StreamReadExactError::FinishedEarly,
quinn::ReadExactError::FinishedEarly(read) => {
StreamReadExactError::FinishedEarly(read)
}
quinn::ReadExactError::ReadError(read) => StreamReadExactError::Read(read.into()),
})
}
Expand Down Expand Up @@ -533,7 +543,7 @@ pub mod session {
self.proto.request()
}

pub async fn finish(mut self) {
pub async fn finish(&mut self) {
let _ = self.stream.0.finish().await;
}
}
Expand All @@ -543,8 +553,9 @@ impl From<quinn::WriteError> for StreamWriteError {
fn from(error: quinn::WriteError) -> Self {
match error {
quinn::WriteError::Stopped(code) => StreamWriteError::Stopped(varint_q2w(code)),
quinn::WriteError::ConnectionLost(_) => StreamWriteError::NotConnected,
quinn::WriteError::UnknownStream => StreamWriteError::QuicProto,
quinn::WriteError::ConnectionLost(_) | quinn::WriteError::ClosedStream => {
StreamWriteError::NotConnected
}
quinn::WriteError::ZeroRttRejected => StreamWriteError::QuicProto,
}
}
Expand All @@ -554,8 +565,9 @@ impl From<quinn::ReadError> for StreamReadError {
fn from(error: quinn::ReadError) -> Self {
match error {
quinn::ReadError::Reset(code) => StreamReadError::Reset(varint_q2w(code)),
quinn::ReadError::ConnectionLost(_) => StreamReadError::NotConnected,
quinn::ReadError::UnknownStream => StreamReadError::QuicProto,
quinn::ReadError::ConnectionLost(_) | quinn::ReadError::ClosedStream => {
StreamReadError::NotConnected
}
quinn::ReadError::IllegalOrderedRead => StreamReadError::QuicProto,
quinn::ReadError::ZeroRttRejected => StreamReadError::QuicProto,
}
Expand Down
4 changes: 2 additions & 2 deletions wtransport/src/driver/streams/qpack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl RemoteQPackEncStream {
loop {
match stream.stream_mut().read_exact(&mut self.buffer).await {
Ok(()) => {}
Err(StreamReadExactError::FinishedEarly) => {
Err(StreamReadExactError::FinishedEarly(_)) => {
return DriverError::Proto(ErrorCode::ClosedCriticalStream);
}
Err(StreamReadExactError::Read(StreamReadError::NotConnected)) => {
Expand Down Expand Up @@ -88,7 +88,7 @@ impl RemoteQPackDecStream {
loop {
match stream.stream_mut().read_exact(&mut self.buffer).await {
Ok(()) => {}
Err(StreamReadExactError::FinishedEarly) => {
Err(StreamReadExactError::FinishedEarly(_)) => {
return DriverError::Proto(ErrorCode::ClosedCriticalStream);
}
Err(StreamReadExactError::Read(StreamReadError::NotConnected)) => {
Expand Down
4 changes: 3 additions & 1 deletion wtransport/src/driver/streams/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ impl LocalSettingsStream {
match self.stream.as_mut() {
Some(stream) => match stream.stopped().await {
StreamWriteError::NotConnected => DriverError::NotConnected,
StreamWriteError::Stopped(_) => DriverError::Proto(ErrorCode::ClosedCriticalStream),
StreamWriteError::Closed | StreamWriteError::Stopped(_) => {
DriverError::Proto(ErrorCode::ClosedCriticalStream)
}
StreamWriteError::QuicProto => DriverError::Proto(ErrorCode::ClosedCriticalStream),
},
None => pending().await,
Expand Down
77 changes: 63 additions & 14 deletions wtransport/src/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use socket2::Socket;
use socket2::Type as SocketType;
use std::collections::HashMap;
use std::future::Future;
use std::future::IntoFuture;
use std::marker::PhantomData;
use std::net::SocketAddr;
use std::net::SocketAddrV4;
Expand Down Expand Up @@ -144,6 +145,11 @@ impl<Side> Endpoint<Side> {
pub fn local_addr(&self) -> std::io::Result<SocketAddr> {
self.endpoint.local_addr()
}

/// Get the number of connections that are currently open.
pub fn open_connections(&self) -> usize {
self.endpoint.open_connections()
}
}

impl Endpoint<endpoint_side::Server> {
Expand Down Expand Up @@ -171,15 +177,15 @@ impl Endpoint<endpoint_side::Server> {

/// Get the next incoming connection attempt from a client.
pub async fn accept(&self) -> IncomingSession {
let quic_connecting = self
let quic_incoming = self
.endpoint
.accept()
.await
.expect("Endpoint cannot be closed");

debug!("New incoming QUIC connection");

IncomingSession::new(quic_connecting)
IncomingSession(quic_incoming)
}

/// Reloads the server configuration.
Expand All @@ -204,11 +210,6 @@ impl Endpoint<endpoint_side::Server> {

Ok(())
}

/// Rejects new incoming connections without affecting existing connections
pub fn reject_new_connections(&self) {
self.endpoint.reject_new_connections();
}
}

impl Endpoint<endpoint_side::Client> {
Expand Down Expand Up @@ -564,18 +565,66 @@ where
type DynFutureIncomingSession =
dyn Future<Output = Result<SessionRequest, ConnectionError>> + Send + Sync;

/// [`Future`] for an in-progress incoming connection attempt.
/// [`IntoFuture`] for an in-progress incoming connection attempt.
///
/// Created by [`Endpoint::accept`].
pub struct IncomingSession(Pin<Box<DynFutureIncomingSession>>);
pub struct IncomingSession(quinn::Incoming);
BiagioFesta marked this conversation as resolved.
Show resolved Hide resolved

impl IncomingSession {
fn new(quic_connecting: quinn::Connecting) -> Self {
Self(Box::pin(Self::accept(quic_connecting)))
/// The peer's UDP address.
pub fn remote_address(&self) -> SocketAddr {
self.0.remote_address()
}

/// Whether the socket address that is initiating this connection has been validated.
///
/// This means that the sender of the initial packet has proved that they can receive traffic
/// sent to `self.remote_address()`.
pub fn remote_address_validated(&self) -> bool {
self.0.remote_address_validated()
}

/// Respond with a retry packet, requiring the client to retry with address validation
///
/// # Panics
///
/// If `remote_address_validated()` is true.
pub fn retry(self) {
self.0.retry().expect("remote address already verified");
}

/// Reject this incoming connection attempt.
pub fn refuse(self) {
self.0.refuse();
}

/// Ignore this incoming connection attempt, not sending any packet in response.
pub fn ignore(self) {
self.0.ignore();
}
}

impl IntoFuture for IncomingSession {
type IntoFuture = IncomingSessionFuture;
type Output = Result<SessionRequest, ConnectionError>;

fn into_future(self) -> Self::IntoFuture {
IncomingSessionFuture::new(self.0)
}
}

/// [`Future`] for an in-progress incoming connection attempt.
///
/// Created by awaiting an [`IncomingSession`]
pub struct IncomingSessionFuture(Pin<Box<DynFutureIncomingSession>>);
finnbear marked this conversation as resolved.
Show resolved Hide resolved

impl IncomingSessionFuture {
fn new(quic_incoming: quinn::Incoming) -> Self {
Self(Box::pin(Self::accept(quic_incoming)))
}

async fn accept(quic_connecting: quinn::Connecting) -> Result<SessionRequest, ConnectionError> {
let quic_connection = quic_connecting.await?;
async fn accept(quic_incoming: quinn::Incoming) -> Result<SessionRequest, ConnectionError> {
let quic_connection = quic_incoming.await?;

let driver = Driver::init(quic_connection.clone());

Expand All @@ -593,7 +642,7 @@ impl IncomingSession {
}
}

impl Future for IncomingSession {
impl Future for IncomingSessionFuture {
type Output = Result<SessionRequest, ConnectionError>;

fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Expand Down
Loading
Loading