quinn: allow unsecure connections in WebTransport elements

WebTransport requires a secure connection, but certificates
can have a validity of 2 weeks. For testing, a new property
is added to allow unsecure connections.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1867>
This commit is contained in:
Andoni Morales Alastruey 2024-11-28 16:54:37 +00:00 committed by GStreamer Marge Bot
parent be02c0e388
commit a791cfff2b
5 changed files with 59 additions and 50 deletions

View file

@ -57,6 +57,7 @@ struct Settings {
bind_port: u16,
certificate_file: Option<PathBuf>,
keep_alive_interval: u64,
secure_conn: bool,
timeout: u32,
transport_config: QuinnQuicTransportConfig,
url: String,
@ -77,6 +78,7 @@ impl Default for Settings {
bind_port: DEFAULT_BIND_PORT,
certificate_file: None,
keep_alive_interval: 0,
secure_conn: DEFAULT_SECURE_CONNECTION,
timeout: DEFAULT_TIMEOUT,
transport_config,
url: DEFAULT_ADDR.to_string(),
@ -131,6 +133,29 @@ impl ElementImpl for QuinnWebTransportClientSrc {
PAD_TEMPLATES.as_ref()
}
fn change_state(
&self,
transition: gst::StateChange,
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
if transition == gst::StateChange::NullToReady {
let settings = self.settings.lock().unwrap();
/*
* Fail the state change if a secure connection was requested but
* no certificate path was provided.
*/
if settings.secure_conn && settings.certificate_file.is_none() {
gst::error!(
CAT,
imp = self,
"Certificate or private key file not provided for secure connection"
);
return Err(gst::StateChangeError);
}
}
self.parent_change_state(transition)
}
}
impl ObjectImpl for QuinnWebTransportClientSrc {
@ -175,6 +200,11 @@ impl ObjectImpl for QuinnWebTransportClientSrc {
.blurb("Connection statistics")
.read_only()
.build(),
glib::ParamSpecBoolean::builder("secure-connection")
.nick("Use secure connection.")
.blurb("Use certificates for QUIC connection. False: Insecure connection, True: Secure connection.")
.default_value(DEFAULT_SECURE_CONNECTION)
.build(),
]
});
@ -195,6 +225,9 @@ impl ObjectImpl for QuinnWebTransportClientSrc {
"timeout" => {
settings.timeout = value.get().expect("type checked upstream");
}
"secure-connection" => {
settings.secure_conn = value.get().expect("type checked upstream");
}
"url" => {
settings.url = value.get::<String>().expect("type checked upstream");
}
@ -217,6 +250,7 @@ impl ObjectImpl for QuinnWebTransportClientSrc {
"timeout" => settings.timeout.to_value(),
"url" => settings.url.to_value(),
"use-datagram" => settings.use_datagram.to_value(),
"secure-connection" => settings.secure_conn.to_value(),
"stats" => {
let state = self.state.lock().unwrap();
match *state {
@ -471,7 +505,7 @@ impl QuinnWebTransportClientSrc {
server_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 4443), // This will be filled in correctly later
server_name: DEFAULT_SERVER_NAME.to_string(),
client_addr: Some(client_addr),
secure_conn: true,
secure_conn: settings.secure_conn,
alpns: vec![HTTP3_ALPN.to_string()],
certificate_file: settings.certificate_file.clone(),
private_key_file: None,

View file

@ -21,7 +21,6 @@ use futures::future;
use gst::{glib, prelude::*, subclass::prelude::*};
use gst_base::subclass::prelude::*;
use quinn::{Connection, TransportConfig};
use web_transport_quinn::{Request, SendStream, Session};
use std::path::PathBuf;
use std::sync::{LazyLock, Mutex};
use web_transport_quinn::{Request, SendStream, Session};
@ -55,6 +54,7 @@ struct Settings {
use_datagram: bool,
certificate_file: Option<PathBuf>,
private_key_file: Option<PathBuf>,
secure_conn: bool,
transport_config: QuinnQuicTransportConfig,
drop_buffer_for_datagram: bool,
}
@ -74,6 +74,7 @@ impl Default for Settings {
use_datagram: false,
certificate_file: None,
private_key_file: None,
secure_conn: DEFAULT_SECURE_CONNECTION,
transport_config,
drop_buffer_for_datagram: DEFAULT_DROP_BUFFER_FOR_DATAGRAM,
}
@ -133,11 +134,14 @@ impl ElementImpl for QuinnWebTransportServerSink {
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
if transition == gst::StateChange::NullToReady {
let settings = self.settings.lock().unwrap();
/*
* WebTransport requires a secure connection, fail the state change if
* no certificate paths were provided.
* Fail the state change if a secure connection was requested but
* no certificate path was provided.
*/
if settings.certificate_file.is_none() || settings.private_key_file.is_none() {
if settings.secure_conn
&& (settings.certificate_file.is_none() || settings.private_key_file.is_none())
{
gst::error!(
CAT,
imp = self,
@ -237,6 +241,11 @@ impl ObjectImpl for QuinnWebTransportServerSink {
.blurb("Drop buffers when using datagram if buffer size > max datagram size")
.default_value(DEFAULT_DROP_BUFFER_FOR_DATAGRAM)
.build(),
glib::ParamSpecBoolean::builder("secure-connection")
.nick("Use secure connection.")
.blurb("Use certificates for QUIC connection. False: Insecure connection, True: Secure connection.")
.default_value(DEFAULT_SECURE_CONNECTION)
.build(),
]
});
@ -299,6 +308,9 @@ impl ObjectImpl for QuinnWebTransportServerSink {
"drop-buffer-for-datagram" => {
settings.drop_buffer_for_datagram = value.get().expect("type checked upstream");
}
"secure-connection" => {
settings.secure_conn = value.get().expect("type checked upstream");
}
_ => unimplemented!(),
}
}
@ -335,6 +347,7 @@ impl ObjectImpl for QuinnWebTransportServerSink {
"datagram-send-buffer-size" => {
(settings.transport_config.datagram_send_buffer_size as u64).to_value()
}
"secure-connection" => settings.secure_conn.to_value(),
"stats" => {
let state = self.state.lock().unwrap();
match *state {
@ -564,6 +577,7 @@ impl QuinnWebTransportServerSink {
let (use_datagram, endpoint_config) = {
let settings = self.settings.lock().unwrap();
let secure_conn = settings.secure_conn;
let server_addr =
make_socket_addr(format!("{}:{}", settings.address, settings.port).as_str())?;
@ -573,7 +587,7 @@ impl QuinnWebTransportServerSink {
server_addr,
server_name: settings.server_name.clone(),
client_addr: None,
secure_conn: true,
secure_conn,
alpns: vec![HTTP3_ALPN.to_string()],
certificate_file: settings.certificate_file.clone(),
private_key_file: settings.private_key_file.clone(),

View file

@ -1,10 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIBZjCCAQ2gAwIBAgIUducmm59E2q/s87I6F8uymGvNuM4wCgYIKoZIzj0EAwIw
FDESMBAGA1UEAwwJTG9jYWxob3N0MB4XDTI0MTAxNjA4MDExMFoXDTI0MTExNTA4
MDExMFowFDESMBAGA1UEAwwJTG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0D
AQcDQgAEEmItpfgNKextROvDh4+phcUP3s0jb1OeQHuHnYa5hw5O+FVQf6FtgL6U
Zx5fdgl+NIW795ZR/Nt7y9D5H4fxP6M9MDswGgYDVR0RBBMwEYIJbG9jYWxob3N0
hwR/AAABMB0GA1UdDgQWBBSLX7qrn+1oorVuolaoiKVBU6irfTAKBggqhkjOPQQD
AgNHADBEAiASp7doLyxErcAfUJ3QLxQZ+Rav8+n/Xv7ukisxNr+UuAIgALEzXXsa
mN1VcFcbHPcQoTwCFlv/MxKAmaOgJel43tc=
-----END CERTIFICATE-----

View file

@ -1,8 +0,0 @@
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEINlRLGfOFJfJFhcOYkW1PXw6OPM5qABO0FT158tiI25hoAoGCCqGSM49
AwEHoUQDQgAEEmItpfgNKextROvDh4+phcUP3s0jb1OeQHuHnYa5hw5O+FVQf6Ft
gL6UZx5fdgl+NIW795ZR/Nt7y9D5H4fxPw==
-----END EC PRIVATE KEY-----

View file

@ -9,7 +9,7 @@
use gst::prelude::*;
use serial_test::serial;
use std::{path::PathBuf, thread};
use std::thread;
fn init() {
use std::sync::Once;
@ -27,40 +27,19 @@ fn make_buffer(content: &[u8]) -> gst::Buffer {
buf
}
fn get_certificates_paths() -> (String, String) {
let mut certs_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
certs_dir.push("tests");
certs_dir.push("certs");
(
certs_dir
.join("localhost.crt")
.into_os_string()
.into_string()
.unwrap(),
certs_dir
.join("localhost.key")
.into_os_string()
.into_string()
.unwrap(),
)
}
fn send_receive(src_pipeline_props: &str, sink_pipeline_props: &str) {
init();
let content = "Hello, world!\n".as_bytes();
let (cert_path, key_path) = get_certificates_paths();
let src_pipeline = format!(
"quinnwtclientsrc {} certificate-file={} caps=text/plain",
src_pipeline_props, cert_path
"quinnwtclientsrc {} secure-connection=false",
src_pipeline_props
);
let sink_pipeline = format!(
"quinnwtserversink {} server-name=localhost \
address=127.0.0.1 certificate-file={} private-key-file={}",
sink_pipeline_props, cert_path, key_path
address=127.0.0.1 secure-connection=false",
sink_pipeline_props
);
thread::spawn(move || {