From 75b25d011fde05ffbfb83e4f478d48ad0a37d56c Mon Sep 17 00:00:00 2001 From: Tamas Levai Date: Mon, 12 Feb 2024 20:14:33 +0100 Subject: [PATCH] net/quic: Allow specifying an ALPN transport parameter See https://datatracker.ietf.org/doc/html/rfc9000#section-7.4. This controls the Transport Layer Security (TLS) extension for application-layer protocol negotiation within the TLS handshake. Part-of: --- net/quic/src/quicsink/imp.rs | 19 ++++++++++++++++++- net/quic/src/quicsrc/imp.rs | 18 ++++++++++++++++++ net/quic/src/utils.rs | 23 ++++++++++++++++++----- 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/net/quic/src/quicsink/imp.rs b/net/quic/src/quicsink/imp.rs index ee53b804..382985cc 100644 --- a/net/quic/src/quicsink/imp.rs +++ b/net/quic/src/quicsink/imp.rs @@ -22,6 +22,7 @@ use std::sync::Mutex; static DEFAULT_SERVER_NAME: &str = "localhost"; static DEFAULT_SERVER_ADDR: &str = "127.0.0.1:5000"; static DEFAULT_CLIENT_ADDR: &str = "127.0.0.1:5001"; +const DEFAULT_ALPN: &str = "h3"; const DEFAULT_TIMEOUT: u32 = 15; const DEFAULT_SECURE_CONNECTION: bool = true; @@ -46,6 +47,7 @@ struct Settings { client_address: SocketAddr, server_address: SocketAddr, server_name: String, + alpn: String, timeout: u32, secure_conn: bool, use_datagram: bool, @@ -57,6 +59,7 @@ impl Default for Settings { client_address: DEFAULT_CLIENT_ADDR.parse::().unwrap(), server_address: DEFAULT_SERVER_ADDR.parse::().unwrap(), server_name: DEFAULT_SERVER_NAME.to_string(), + alpn: DEFAULT_ALPN.to_string(), timeout: DEFAULT_TIMEOUT, secure_conn: DEFAULT_SECURE_CONNECTION, use_datagram: false, @@ -132,6 +135,10 @@ impl ObjectImpl for QuicSink { .nick("QUIC client address") .blurb("Address to be used by this QUIC client e.g. 127.0.0.1:5001") .build(), + glib::ParamSpecString::builder("alpn") + .nick("QUIC ALPN value") + .blurb("QUIC connection Application-Layer Protocol Negotiation (ALPN) value") + .build(), glib::ParamSpecUInt::builder("timeout") .nick("Timeout") .blurb("Value in seconds to timeout QUIC endpoint requests (0 = No timeout).") @@ -191,6 +198,10 @@ impl ObjectImpl for QuicSink { ), } } + "alpn" => { + let mut settings = self.settings.lock().unwrap(); + settings.alpn = value.get::().expect("type checked upstream"); + } "timeout" => { let mut settings = self.settings.lock().unwrap(); settings.timeout = value.get().expect("type checked upstream"); @@ -221,6 +232,10 @@ impl ObjectImpl for QuicSink { let settings = self.settings.lock().unwrap(); settings.client_address.to_string().to_value() } + "alpn" => { + let settings = self.settings.lock().unwrap(); + settings.alpn.to_value() + } "timeout" => { let settings = self.settings.lock().unwrap(); settings.timeout.to_value() @@ -424,6 +439,7 @@ impl QuicSink { let client_addr; let server_addr; let server_name; + let alpn; let use_datagram; let secure_conn; @@ -432,11 +448,12 @@ impl QuicSink { client_addr = settings.client_address; server_addr = settings.server_address; server_name = settings.server_name.clone(); + alpn = settings.alpn.clone(); use_datagram = settings.use_datagram; secure_conn = settings.secure_conn; } - let endpoint = client_endpoint(client_addr, secure_conn).map_err(|err| { + let endpoint = client_endpoint(client_addr, secure_conn, &alpn).map_err(|err| { WaitError::FutureError(gst::error_msg!( gst::ResourceError::Failed, ["Failed to configure endpoint: {}", err] diff --git a/net/quic/src/quicsrc/imp.rs b/net/quic/src/quicsrc/imp.rs index 3dc666a3..38e6ea9a 100644 --- a/net/quic/src/quicsrc/imp.rs +++ b/net/quic/src/quicsrc/imp.rs @@ -25,6 +25,7 @@ use std::sync::Mutex; static DEFAULT_SERVER_NAME: &str = "localhost"; static DEFAULT_SERVER_ADDR: &str = "127.0.0.1:5000"; +const DEFAULT_ALPN: &str = "h3"; const DEFAULT_TIMEOUT: u32 = 15; const DEFAULT_PRIVATE_KEY_TYPE: QuicPrivateKeyType = QuicPrivateKeyType::Pkcs8; const DEFAULT_SECURE_CONNECTION: bool = true; @@ -53,6 +54,7 @@ enum State { struct Settings { server_address: SocketAddr, server_name: String, + alpn: String, timeout: u32, secure_conn: bool, caps: gst::Caps, @@ -66,6 +68,7 @@ impl Default for Settings { Settings { server_address: DEFAULT_SERVER_ADDR.parse::().unwrap(), server_name: DEFAULT_SERVER_NAME.to_string(), + alpn: DEFAULT_ALPN.to_string(), timeout: DEFAULT_TIMEOUT, secure_conn: DEFAULT_SECURE_CONNECTION, caps: gst::Caps::new_any(), @@ -167,6 +170,10 @@ impl ObjectImpl for QuicSrc { .nick("QUIC server address") .blurb("Address of the QUIC server to connect to e.g. 127.0.0.1:5000") .build(), + glib::ParamSpecString::builder("alpn") + .nick("QUIC ALPN value") + .blurb("QUIC connection Application-Layer Protocol Negotiation (ALPN) value") + .build(), glib::ParamSpecUInt::builder("timeout") .nick("Timeout") .blurb("Value in seconds to timeout QUIC endpoint requests (0 = No timeout).") @@ -223,6 +230,10 @@ impl ObjectImpl for QuicSrc { ), } } + "alpn" => { + let mut settings = self.settings.lock().unwrap(); + settings.alpn = value.get::().expect("type checked upstream"); + } "caps" => { let mut settings = self.settings.lock().unwrap(); settings.caps = value @@ -270,6 +281,10 @@ impl ObjectImpl for QuicSrc { let settings = self.settings.lock().unwrap(); settings.server_address.to_string().to_value() } + "alpn" => { + let settings = self.settings.lock().unwrap(); + settings.alpn.to_value() + } "caps" => { let settings = self.settings.lock().unwrap(); settings.caps.to_value() @@ -520,6 +535,7 @@ impl QuicSrc { async fn wait_for_connection(&self) -> Result<(Connection, Option), WaitError> { let server_addr; let server_name; + let alpn; let use_datagram; let secure_conn; let cert_path; @@ -529,6 +545,7 @@ impl QuicSrc { let settings = self.settings.lock().unwrap(); server_addr = settings.server_address; server_name = settings.server_name.clone(); + alpn = settings.alpn.clone(); use_datagram = settings.use_datagram; secure_conn = settings.secure_conn; cert_path = settings.certificate_path.clone(); @@ -539,6 +556,7 @@ impl QuicSrc { server_addr, &server_name, secure_conn, + &alpn, cert_path, private_key_type, ) diff --git a/net/quic/src/utils.rs b/net/quic/src/utils.rs index 0932408a..18ff2094 100644 --- a/net/quic/src/utils.rs +++ b/net/quic/src/utils.rs @@ -131,14 +131,15 @@ impl rustls::client::ServerCertVerifier for SkipServerVerification { } } -fn configure_client(secure_conn: bool) -> Result> { +fn configure_client(secure_conn: bool, alpn: &str) -> Result> { if secure_conn { Ok(ClientConfig::with_native_roots()) } else { - let crypto = rustls::ClientConfig::builder() + let mut crypto = rustls::ClientConfig::builder() .with_safe_defaults() .with_custom_certificate_verifier(SkipServerVerification::new()) .with_no_client_auth(); + crypto.alpn_protocols = vec![alpn.as_bytes().to_vec()]; Ok(ClientConfig::new(Arc::new(crypto))) } @@ -201,6 +202,7 @@ fn read_certs_from_file( fn configure_server( server_name: &str, secure_conn: bool, + alpn: &str, certificate_path: Option, private_key_type: QuicPrivateKeyType, ) -> Result<(ServerConfig, Vec), Box> { @@ -216,7 +218,16 @@ fn configure_server( (cert_chain, priv_key) }; - let mut server_config = ServerConfig::with_single_cert(cert.clone(), key)?; + let mut crypto = rustls::ServerConfig::builder() + .with_safe_default_cipher_suites() + .with_safe_default_kx_groups() + .with_protocol_versions(&[&rustls::version::TLS13]) + .unwrap() + .with_no_client_auth() + .with_single_cert(cert.clone(), key)?; + crypto.alpn_protocols = vec![alpn.as_bytes().to_vec()]; + let mut server_config = ServerConfig::with_crypto(Arc::new(crypto)); + Arc::get_mut(&mut server_config.transport) .unwrap() .max_concurrent_bidi_streams(0_u8.into()) @@ -229,11 +240,12 @@ pub fn server_endpoint( server_addr: SocketAddr, server_name: &str, secure_conn: bool, + alpn: &str, certificate_path: Option, private_key_type: QuicPrivateKeyType, ) -> Result> { let (server_config, _) = - configure_server(server_name, secure_conn, certificate_path, private_key_type)?; + configure_server(server_name, secure_conn, alpn, certificate_path, private_key_type)?; let endpoint = Endpoint::server(server_config, server_addr)?; Ok(endpoint) @@ -242,8 +254,9 @@ pub fn server_endpoint( pub fn client_endpoint( client_addr: SocketAddr, secure_conn: bool, + alpn: &str, ) -> Result> { - let client_cfg = configure_client(secure_conn)?; + let client_cfg = configure_client(secure_conn, alpn)?; let mut endpoint = Endpoint::client(client_addr)?; endpoint.set_default_client_config(client_cfg);