moqtap_client/transport/
quic.rs1use bytes::Bytes;
4
5use super::{RecvStream, SendStream, TransportError};
6
7pub struct QuicTransport {
9 conn: quinn::Connection,
10}
11
12impl QuicTransport {
13 pub fn new(conn: quinn::Connection) -> Self {
15 Self { conn }
16 }
17
18 pub async fn open_bi(&self) -> Result<(SendStream, RecvStream), TransportError> {
20 let (send, recv) = self.conn.open_bi().await.map_err(conn_err)?;
21 Ok((SendStream::Quic(send), RecvStream::Quic(recv)))
22 }
23
24 pub async fn accept_bi(&self) -> Result<(SendStream, RecvStream), TransportError> {
26 let (send, recv) = self.conn.accept_bi().await.map_err(conn_err)?;
27 Ok((SendStream::Quic(send), RecvStream::Quic(recv)))
28 }
29
30 pub async fn open_uni(&self) -> Result<SendStream, TransportError> {
32 let send = self.conn.open_uni().await.map_err(conn_err)?;
33 Ok(SendStream::Quic(send))
34 }
35
36 pub async fn accept_uni(&self) -> Result<RecvStream, TransportError> {
38 let recv = self.conn.accept_uni().await.map_err(conn_err)?;
39 Ok(RecvStream::Quic(recv))
40 }
41
42 pub fn send_datagram(&self, data: Bytes) -> Result<(), TransportError> {
44 self.conn.send_datagram(data).map_err(|e| TransportError::SendDatagram(e.to_string()))
45 }
46
47 pub async fn recv_datagram(&self) -> Result<Bytes, TransportError> {
49 self.conn.read_datagram().await.map_err(conn_err)
50 }
51
52 pub fn close(&self, code: u32, reason: &[u8]) {
54 self.conn.close(quinn::VarInt::from_u32(code), reason);
55 }
56}
57
58fn conn_err(e: quinn::ConnectionError) -> TransportError {
60 TransportError::Connection(e.to_string())
61}
62
63impl From<quinn::ConnectionError> for TransportError {
66 fn from(e: quinn::ConnectionError) -> Self {
67 TransportError::Connection(e.to_string())
68 }
69}
70
71impl From<quinn::WriteError> for TransportError {
72 fn from(e: quinn::WriteError) -> Self {
76 match e {
77 quinn::WriteError::Stopped(code) => TransportError::Stopped(code.into_inner()),
78 other => TransportError::Write(other.to_string()),
79 }
80 }
81}
82
83impl From<quinn::ReadError> for TransportError {
84 fn from(e: quinn::ReadError) -> Self {
88 match e {
89 quinn::ReadError::Reset(code) => TransportError::StreamReset(code.into_inner()),
90 other => TransportError::Read(other.to_string()),
91 }
92 }
93}
94
95impl From<quinn::ReadExactError> for TransportError {
96 fn from(e: quinn::ReadExactError) -> Self {
97 match e {
98 quinn::ReadExactError::ReadError(inner) => inner.into(),
99 other => TransportError::Read(other.to_string()),
100 }
101 }
102}
103
104impl From<quinn::ConnectError> for TransportError {
105 fn from(e: quinn::ConnectError) -> Self {
106 TransportError::Connect(e.to_string())
107 }
108}
109
110impl From<quinn::ClosedStream> for TransportError {
111 fn from(_e: quinn::ClosedStream) -> Self {
112 TransportError::StreamClosed
113 }
114}
115
116impl From<quinn::SendDatagramError> for TransportError {
117 fn from(e: quinn::SendDatagramError) -> Self {
118 TransportError::SendDatagram(e.to_string())
119 }
120}
121
122pub struct QuicDialOptions {
132 pub skip_cert_verification: bool,
134 pub ca_certs: Vec<Vec<u8>>,
136 pub alpn: Vec<Vec<u8>>,
142}
143
144#[derive(Debug, thiserror::Error)]
150pub enum DialError {
151 #[error("invalid address: {0}")]
153 InvalidAddress(String),
154 #[error("TLS configuration error: {0}")]
156 TlsConfig(String),
157 #[error(transparent)]
159 Transport(#[from] TransportError),
160}
161
162pub async fn dial_quic(
173 addr: &str,
174 options: &QuicDialOptions,
175) -> Result<(super::Transport, Option<Vec<u8>>), DialError> {
176 use std::sync::Arc;
177
178 let server_addr = addr
179 .parse()
180 .map_err(|e: std::net::AddrParseError| DialError::InvalidAddress(e.to_string()))?;
181
182 let mut tls_config = if options.skip_cert_verification {
183 rustls::ClientConfig::builder()
184 .dangerous()
185 .with_custom_certificate_verifier(Arc::new(SkipVerification))
186 .with_no_client_auth()
187 } else {
188 let mut roots = rustls::RootCertStore::empty();
189 roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
190 for der in &options.ca_certs {
191 roots
192 .add(rustls::pki_types::CertificateDer::from(der.clone()))
193 .map_err(|e| DialError::TlsConfig(format!("bad CA cert: {e}")))?;
194 }
195 rustls::ClientConfig::builder().with_root_certificates(roots).with_no_client_auth()
196 };
197
198 tls_config.alpn_protocols = options.alpn.clone();
199
200 let quic_config: quinn::crypto::rustls::QuicClientConfig =
201 tls_config.try_into().map_err(|e| DialError::TlsConfig(format!("{e}")))?;
202 let client_config = quinn::ClientConfig::new(Arc::new(quic_config));
203
204 let mut endpoint = quinn::Endpoint::client("0.0.0.0:0".parse().unwrap())
205 .map_err(|e| DialError::InvalidAddress(e.to_string()))?;
206 endpoint.set_default_client_config(client_config);
207
208 let server_name = addr.split(':').next().unwrap_or("localhost").to_string();
209
210 let quic = endpoint
211 .connect(server_addr, &server_name)
212 .map_err(TransportError::from)?
213 .await
214 .map_err(TransportError::from)?;
215
216 let negotiated = negotiated_alpn(&quic);
217 Ok((super::Transport::Quic(QuicTransport::new(quic)), negotiated))
218}
219
220fn negotiated_alpn(conn: &quinn::Connection) -> Option<Vec<u8>> {
222 conn.handshake_data()?.downcast::<quinn::crypto::rustls::HandshakeData>().ok()?.protocol
223}
224
225#[derive(Debug)]
227struct SkipVerification;
228
229impl rustls::client::danger::ServerCertVerifier for SkipVerification {
230 fn verify_server_cert(
231 &self,
232 _end_entity: &rustls::pki_types::CertificateDer<'_>,
233 _intermediates: &[rustls::pki_types::CertificateDer<'_>],
234 _server_name: &rustls::pki_types::ServerName<'_>,
235 _ocsp_response: &[u8],
236 _now: rustls::pki_types::UnixTime,
237 ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
238 Ok(rustls::client::danger::ServerCertVerified::assertion())
239 }
240
241 fn verify_tls12_signature(
242 &self,
243 _message: &[u8],
244 _cert: &rustls::pki_types::CertificateDer<'_>,
245 _dcs: &rustls::DigitallySignedStruct,
246 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
247 Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
248 }
249
250 fn verify_tls13_signature(
251 &self,
252 _message: &[u8],
253 _cert: &rustls::pki_types::CertificateDer<'_>,
254 _dcs: &rustls::DigitallySignedStruct,
255 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
256 Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
257 }
258
259 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
260 vec![
261 rustls::SignatureScheme::ECDSA_NISTP256_SHA256,
262 rustls::SignatureScheme::ECDSA_NISTP384_SHA384,
263 rustls::SignatureScheme::ED25519,
264 rustls::SignatureScheme::RSA_PSS_SHA256,
265 rustls::SignatureScheme::RSA_PSS_SHA384,
266 rustls::SignatureScheme::RSA_PSS_SHA512,
267 ]
268 }
269}