From ce930eab5fe4b1c69bffc9a8ec34f2156411829c Mon Sep 17 00:00:00 2001 From: Sanchayan Maity Date: Wed, 14 Feb 2024 13:08:37 +0530 Subject: [PATCH] net/quic: Allow setting multiple ALPN transport parameters For reference, see https://datatracker.ietf.org/doc/html/rfc9000#section-7.4 https://datatracker.ietf.org/doc/html/rfc7301 Part-of: --- net/quic/src/quicsink/imp.rs | 44 ++++++++++++++++++++++++++---------- net/quic/src/quicsrc/imp.rs | 44 ++++++++++++++++++++++++++---------- net/quic/src/utils.rs | 29 +++++++++++++++++------- 3 files changed, 85 insertions(+), 32 deletions(-) diff --git a/net/quic/src/quicsink/imp.rs b/net/quic/src/quicsink/imp.rs index 382985cc..95eb2cf0 100644 --- a/net/quic/src/quicsink/imp.rs +++ b/net/quic/src/quicsink/imp.rs @@ -22,6 +22,14 @@ 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"; +/* + * For QUIC transport parameters + * + * + * A HTTP client might specify "http/1.1" and/or "h2" or "h3". + * Other well-known values are listed in the at IANA registry at + * . + */ const DEFAULT_ALPN: &str = "h3"; const DEFAULT_TIMEOUT: u32 = 15; const DEFAULT_SECURE_CONNECTION: bool = true; @@ -47,7 +55,7 @@ struct Settings { client_address: SocketAddr, server_address: SocketAddr, server_name: String, - alpn: String, + alpns: Vec, timeout: u32, secure_conn: bool, use_datagram: bool, @@ -59,7 +67,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(), + alpns: vec![DEFAULT_ALPN.to_string()], timeout: DEFAULT_TIMEOUT, secure_conn: DEFAULT_SECURE_CONNECTION, use_datagram: false, @@ -135,9 +143,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") + gst::ParamSpecArray::builder("alpn-protocols") + .nick("QUIC ALPN values") + .blurb("QUIC connection Application-Layer Protocol Negotiation (ALPN) values") + .element_spec(&glib::ParamSpecString::builder("alpn-protocol").build()) .build(), glib::ParamSpecUInt::builder("timeout") .nick("Timeout") @@ -198,9 +207,19 @@ impl ObjectImpl for QuicSink { ), } } - "alpn" => { + "alpn-protocols" => { let mut settings = self.settings.lock().unwrap(); - settings.alpn = value.get::().expect("type checked upstream"); + settings.alpns = value + .get::() + .expect("type checked upstream") + .as_slice() + .iter() + .map(|alpn| { + alpn.get::<&str>() + .expect("type checked upstream") + .to_string() + }) + .collect::>(); } "timeout" => { let mut settings = self.settings.lock().unwrap(); @@ -232,9 +251,10 @@ impl ObjectImpl for QuicSink { let settings = self.settings.lock().unwrap(); settings.client_address.to_string().to_value() } - "alpn" => { + "alpn-protocols" => { let settings = self.settings.lock().unwrap(); - settings.alpn.to_value() + let alpns = settings.alpns.iter().map(|v| v.as_str()); + gst::Array::new(alpns).to_value() } "timeout" => { let settings = self.settings.lock().unwrap(); @@ -439,7 +459,7 @@ impl QuicSink { let client_addr; let server_addr; let server_name; - let alpn; + let alpns; let use_datagram; let secure_conn; @@ -448,12 +468,12 @@ impl QuicSink { client_addr = settings.client_address; server_addr = settings.server_address; server_name = settings.server_name.clone(); - alpn = settings.alpn.clone(); + alpns = settings.alpns.clone(); use_datagram = settings.use_datagram; secure_conn = settings.secure_conn; } - let endpoint = client_endpoint(client_addr, secure_conn, &alpn).map_err(|err| { + let endpoint = client_endpoint(client_addr, secure_conn, alpns).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 38e6ea9a..8fc48803 100644 --- a/net/quic/src/quicsrc/imp.rs +++ b/net/quic/src/quicsrc/imp.rs @@ -25,6 +25,14 @@ use std::sync::Mutex; static DEFAULT_SERVER_NAME: &str = "localhost"; static DEFAULT_SERVER_ADDR: &str = "127.0.0.1:5000"; +/* + * For QUIC transport parameters + * + * + * A HTTP client might specify "http/1.1" and/or "h2" or "h3". + * Other well-known values are listed in the at IANA registry at + * . + */ const DEFAULT_ALPN: &str = "h3"; const DEFAULT_TIMEOUT: u32 = 15; const DEFAULT_PRIVATE_KEY_TYPE: QuicPrivateKeyType = QuicPrivateKeyType::Pkcs8; @@ -54,7 +62,7 @@ enum State { struct Settings { server_address: SocketAddr, server_name: String, - alpn: String, + alpns: Vec, timeout: u32, secure_conn: bool, caps: gst::Caps, @@ -68,7 +76,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(), + alpns: vec![DEFAULT_ALPN.to_string()], timeout: DEFAULT_TIMEOUT, secure_conn: DEFAULT_SECURE_CONNECTION, caps: gst::Caps::new_any(), @@ -170,9 +178,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") + gst::ParamSpecArray::builder("alpn-protocols") + .nick("QUIC ALPN values") + .blurb("QUIC connection Application-Layer Protocol Negotiation (ALPN) values") + .element_spec(&glib::ParamSpecString::builder("alpn-protocol").build()) .build(), glib::ParamSpecUInt::builder("timeout") .nick("Timeout") @@ -230,9 +239,19 @@ impl ObjectImpl for QuicSrc { ), } } - "alpn" => { + "alpn-protocols" => { let mut settings = self.settings.lock().unwrap(); - settings.alpn = value.get::().expect("type checked upstream"); + settings.alpns = value + .get::() + .expect("type checked upstream") + .as_slice() + .iter() + .map(|alpn| { + alpn.get::<&str>() + .expect("type checked upstream") + .to_string() + }) + .collect::>() } "caps" => { let mut settings = self.settings.lock().unwrap(); @@ -281,9 +300,10 @@ impl ObjectImpl for QuicSrc { let settings = self.settings.lock().unwrap(); settings.server_address.to_string().to_value() } - "alpn" => { + "alpn-protocols" => { let settings = self.settings.lock().unwrap(); - settings.alpn.to_value() + let alpns = settings.alpns.iter().map(|v| v.as_str()); + gst::Array::new(alpns).to_value() } "caps" => { let settings = self.settings.lock().unwrap(); @@ -535,7 +555,7 @@ impl QuicSrc { async fn wait_for_connection(&self) -> Result<(Connection, Option), WaitError> { let server_addr; let server_name; - let alpn; + let alpns; let use_datagram; let secure_conn; let cert_path; @@ -545,7 +565,7 @@ impl QuicSrc { let settings = self.settings.lock().unwrap(); server_addr = settings.server_address; server_name = settings.server_name.clone(); - alpn = settings.alpn.clone(); + alpns = settings.alpns.clone(); use_datagram = settings.use_datagram; secure_conn = settings.secure_conn; cert_path = settings.certificate_path.clone(); @@ -556,7 +576,7 @@ impl QuicSrc { server_addr, &server_name, secure_conn, - &alpn, + alpns, cert_path, private_key_type, ) diff --git a/net/quic/src/utils.rs b/net/quic/src/utils.rs index 18ff2094..c9da4a3f 100644 --- a/net/quic/src/utils.rs +++ b/net/quic/src/utils.rs @@ -131,7 +131,7 @@ impl rustls::client::ServerCertVerifier for SkipServerVerification { } } -fn configure_client(secure_conn: bool, alpn: &str) -> Result> { +fn configure_client(secure_conn: bool, alpns: Vec) -> Result> { if secure_conn { Ok(ClientConfig::with_native_roots()) } else { @@ -139,7 +139,11 @@ fn configure_client(secure_conn: bool, alpn: &str) -> Result> = alpns + .iter() + .map(|x| x.as_bytes().to_vec()) + .collect::>(); + crypto.alpn_protocols = alpn_protocols; Ok(ClientConfig::new(Arc::new(crypto))) } @@ -202,8 +206,8 @@ fn read_certs_from_file( fn configure_server( server_name: &str, secure_conn: bool, - alpn: &str, certificate_path: Option, + alpns: Vec, private_key_type: QuicPrivateKeyType, ) -> Result<(ServerConfig, Vec), Box> { let (cert, key) = if secure_conn { @@ -225,7 +229,11 @@ fn configure_server( .unwrap() .with_no_client_auth() .with_single_cert(cert.clone(), key)?; - crypto.alpn_protocols = vec![alpn.as_bytes().to_vec()]; + let alpn_protocols: Vec> = alpns + .iter() + .map(|x| x.as_bytes().to_vec()) + .collect::>(); + crypto.alpn_protocols = alpn_protocols; let mut server_config = ServerConfig::with_crypto(Arc::new(crypto)); Arc::get_mut(&mut server_config.transport) @@ -240,12 +248,17 @@ pub fn server_endpoint( server_addr: SocketAddr, server_name: &str, secure_conn: bool, - alpn: &str, + alpns: Vec, certificate_path: Option, private_key_type: QuicPrivateKeyType, ) -> Result> { - let (server_config, _) = - configure_server(server_name, secure_conn, alpn, certificate_path, private_key_type)?; + let (server_config, _) = configure_server( + server_name, + secure_conn, + certificate_path, + alpns, + private_key_type, + )?; let endpoint = Endpoint::server(server_config, server_addr)?; Ok(endpoint) @@ -254,7 +267,7 @@ pub fn server_endpoint( pub fn client_endpoint( client_addr: SocketAddr, secure_conn: bool, - alpn: &str, + alpn: Vec, ) -> Result> { let client_cfg = configure_client(secure_conn, alpn)?; let mut endpoint = Endpoint::client(client_addr)?;