1mod connection;
13mod endpoint;
14mod stream;
15
16pub use connection::Connection;
17pub use endpoint::Endpoint;
18pub use stream::{RecvStream, SendStream};
19
20pub(crate) use connection::{End, Shared};
21
22use std::sync::Arc;
23
24use moq_noq_proto::crypto::rustls::{QuicClientConfig, QuicServerConfig};
25use rustls::pki_types::pem::PemObject;
26use rustls::pki_types::{CertificateDer, PrivateKeyDer};
27
28#[cfg(feature = "qlog")]
29use super::qlog;
30use super::{Congestion, Error, Identity, SEGMENT, Transport, client, endpoint::CID_LEN, server};
31use crate::udp;
32
33const STREAM_WINDOW: u32 = 4 * 1024 * 1024;
35const CONNECTION_WINDOW: u32 = 16 * 1024 * 1024;
37const DATAGRAM_WINDOW: usize = 64 * SEGMENT;
39
40fn ecn_to_noq(ecn: udp::Ecn) -> moq_noq_proto::EcnCodepoint {
41 match ecn {
42 udp::Ecn::Ect0 => moq_noq_proto::EcnCodepoint::Ect0,
43 udp::Ecn::Ect1 => moq_noq_proto::EcnCodepoint::Ect1,
44 udp::Ecn::Ce => moq_noq_proto::EcnCodepoint::Ce,
45 }
46}
47
48fn ecn_from_noq(ecn: moq_noq_proto::EcnCodepoint) -> udp::Ecn {
49 match ecn {
50 moq_noq_proto::EcnCodepoint::Ect0 => udp::Ecn::Ect0,
51 moq_noq_proto::EcnCodepoint::Ect1 => udp::Ecn::Ect1,
52 moq_noq_proto::EcnCodepoint::Ce => udp::Ecn::Ce,
53 }
54}
55
56impl From<moq_noq_proto::ConnectionError> for Error {
57 fn from(err: moq_noq_proto::ConnectionError) -> Self {
58 use moq_noq_proto::ConnectionError;
59 match err {
60 ConnectionError::ApplicationClosed(close) => Self::App {
61 code: close.error_code.into_inner(),
62 reason: String::from_utf8_lossy(&close.reason).into_owned(),
63 },
64 ConnectionError::ConnectionClosed(close) => Self::Transport {
65 code: close.error_code.into(),
66 reason: String::from_utf8_lossy(&close.reason).into_owned(),
67 },
68 ConnectionError::TransportError(err) => Self::Transport {
69 code: err.code.into(),
70 reason: err.reason.clone(),
71 },
72 ConnectionError::TimedOut => Self::TimedOut,
73 err => Self::Quic(err.to_string()),
77 }
78 }
79}
80
81fn provider() -> Arc<rustls::crypto::CryptoProvider> {
86 static PROVIDER: std::sync::OnceLock<Arc<rustls::crypto::CryptoProvider>> = std::sync::OnceLock::new();
87 PROVIDER
88 .get_or_init(|| Arc::new(rustls::crypto::aws_lc_rs::default_provider()))
89 .clone()
90}
91
92pub(crate) fn endpoint_config(
95 shard: Option<moq_sock::shard::Shard>,
96) -> Result<Arc<moq_noq_proto::EndpointConfig>, Error> {
97 let mut config = moq_noq_proto::EndpointConfig::default();
98 config.cid_generator(Arc::new(move || Box::new(Cids { shard })));
99 config
100 .max_udp_payload_size(SEGMENT as u16)
101 .map_err(|err| Error::Quic(err.to_string()))?;
102 Ok(Arc::new(config))
103}
104
105#[derive(Debug)]
111struct Cids {
112 shard: Option<moq_sock::shard::Shard>,
113}
114
115impl moq_noq_proto::ConnectionIdGenerator for Cids {
116 fn generate_cid(&mut self) -> moq_noq_proto::ConnectionId {
117 moq_noq_proto::ConnectionId::new(&super::endpoint::cid(self.shard))
118 }
119
120 fn cid_len(&self) -> usize {
121 CID_LEN
122 }
123
124 fn cid_lifetime(&self) -> Option<std::time::Duration> {
125 None
126 }
127}
128
129pub(crate) fn client_config(config: &client::Config) -> Result<moq_noq_proto::ClientConfig, Error> {
131 let provider = provider();
132 let builder = rustls::ClientConfig::builder_with_provider(provider.clone())
133 .with_protocol_versions(&[&rustls::version::TLS13])
134 .map_err(|err| Error::Tls(err.to_string()))?;
135
136 let verifier: Arc<dyn rustls::client::danger::ServerCertVerifier> = match (config.verify, config.system_roots) {
140 (false, _) => Arc::new(NoVerify(provider.clone())),
141 (true, true) if config.roots.is_empty() => Arc::new(
144 rustls_platform_verifier::Verifier::new(provider.clone()).map_err(|err| Error::Tls(err.to_string()))?,
145 ),
146 (true, true) => Arc::new(
147 rustls_platform_verifier::Verifier::new_with_extra_roots(read_roots(&config.roots)?, provider.clone())
148 .map_err(|err| Error::Tls(err.to_string()))?,
149 ),
150 (true, false) => rustls::client::WebPkiServerVerifier::builder_with_provider(
153 Arc::new(root_store(&config.roots)?),
154 provider.clone(),
155 )
156 .build()
157 .map_err(|err| Error::Tls(err.to_string()))?,
158 };
159 let builder = builder.dangerous().with_custom_certificate_verifier(verifier);
160
161 let mut tls = match &config.identity {
162 Some(identity) => {
163 let (chain, key) = keypair(identity)?;
164 builder
165 .with_client_auth_cert(chain, key)
166 .map_err(|err| Error::Tls(err.to_string()))?
167 }
168 None => builder.with_no_client_auth(),
169 };
170 tls.alpn_protocols = alpn(&config.alpn);
171
172 let crypto = QuicClientConfig::try_from(tls).map_err(|err| Error::Tls(err.to_string()))?;
173 let mut client = moq_noq_proto::ClientConfig::new(Arc::new(crypto));
174 let transport = transport_config(&config.transport)?;
175 #[cfg(feature = "qlog")]
176 let transport = with_qlog(transport, &config.transport);
177 client.transport_config(Arc::new(transport));
178 Ok(client)
179}
180
181pub(crate) fn server_config(config: &server::Config) -> Result<moq_noq_proto::ServerConfig, Error> {
183 config.check()?;
184 let provider = provider();
185 let builder = rustls::ServerConfig::builder_with_provider(provider.clone())
186 .with_protocol_versions(&[&rustls::version::TLS13])
187 .map_err(|err| Error::Tls(err.to_string()))?;
188
189 let verifier = match config.client_auth.roots() {
190 None => rustls::server::WebPkiClientVerifier::no_client_auth(),
191 Some((roots, required)) => {
192 let builder =
195 rustls::server::WebPkiClientVerifier::builder_with_provider(Arc::new(root_store(roots)?), provider);
196 let builder = match required {
197 true => builder,
198 false => builder.allow_unauthenticated(),
199 };
200 builder.build().map_err(|err| Error::Tls(err.to_string()))?
201 }
202 };
203
204 let (chain, key) = keypair(&config.identity)?;
205 let mut tls = builder
206 .with_client_cert_verifier(verifier)
207 .with_single_cert(chain, key)
208 .map_err(|err| Error::Tls(err.to_string()))?;
209 tls.alpn_protocols = alpn(&config.alpn);
210
211 let crypto = QuicServerConfig::try_from(tls).map_err(|err| Error::Tls(err.to_string()))?;
212 let mut server = moq_noq_proto::ServerConfig::with_crypto(Arc::new(crypto));
213 let transport = transport_config(&config.transport)?;
214 #[cfg(feature = "qlog")]
215 let transport = with_qlog(transport, &config.transport);
216 server.transport_config(Arc::new(transport));
217 Ok(server)
218}
219
220#[cfg(feature = "qlog")]
224fn with_qlog(mut transport: moq_noq_proto::TransportConfig, config: &Transport) -> moq_noq_proto::TransportConfig {
225 let Some(sink) = config.qlog.clone() else {
226 return transport;
227 };
228
229 transport.qlog_factory(Arc::new(Traces { sink }));
230
231 transport
232}
233
234#[cfg(feature = "qlog")]
237#[derive(Debug)]
238struct Traces {
239 sink: qlog::Sink,
240}
241
242#[cfg(feature = "qlog")]
243impl moq_noq_proto::QlogFactory for Traces {
244 fn for_connection(
245 &self,
246 side: moq_noq_proto::Side,
247 _remote: std::net::SocketAddr,
248 initial_dst_cid: moq_noq_proto::ConnectionId,
249 _now: std::time::Instant,
250 ) -> Option<moq_noq_proto::QlogConfig> {
251 let side = match side {
252 moq_noq_proto::Side::Client => qlog::Side::Client,
253 moq_noq_proto::Side::Server => qlog::Side::Server,
254 };
255 Some(moq_noq_proto::QlogConfig::new(self.sink.trace(&initial_dst_cid, side)))
256 }
257}
258
259fn transport_config(config: &Transport) -> Result<moq_noq_proto::TransportConfig, Error> {
261 use moq_noq_proto::VarInt;
262
263 let idle = moq_noq_proto::IdleTimeout::try_from(config.idle_timeout)
264 .map_err(|_| Error::Quic(format!("idle timeout out of range: {:?}", config.idle_timeout)))?;
265 let streams = VarInt::from_u64(config.max_streams)
266 .map_err(|_| Error::Quic(format!("stream limit out of range: {}", config.max_streams)))?;
267
268 let mut transport = moq_noq_proto::TransportConfig::default();
269 transport.max_idle_timeout(Some(idle));
270 transport.keep_alive_interval(config.keep_alive);
271 transport.max_concurrent_bidi_streams(streams);
272 transport.max_concurrent_uni_streams(streams);
273 transport.stream_receive_window(STREAM_WINDOW.into());
274 transport.receive_window(CONNECTION_WINDOW.into());
275 transport.send_window(CONNECTION_WINDOW.into());
276 transport.datagram_receive_buffer_size(Some(DATAGRAM_WINDOW));
277 transport.datagram_send_buffer_size(DATAGRAM_WINDOW);
278 transport.initial_mtu(SEGMENT as u16);
281 transport.min_mtu(SEGMENT as u16);
282 transport.mtu_discovery_config(None);
283 transport.congestion_controller_factory(match config.congestion {
284 Congestion::Loss => Arc::new(moq_noq_proto::congestion::CubicConfig::default())
285 as Arc<dyn moq_noq_proto::congestion::ControllerFactory + Send + Sync>,
286 Congestion::Delay => Arc::new(moq_noq_proto::congestion::Bbr3Config::default()),
287 });
288 Ok(transport)
289}
290
291fn alpn(protocols: &[String]) -> Vec<Vec<u8>> {
294 protocols.iter().map(|proto| proto.as_bytes().to_vec()).collect()
295}
296
297fn keypair(identity: &Identity) -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>), Error> {
299 let chain = CertificateDer::pem_slice_iter(identity.cert())
300 .collect::<Result<Vec<_>, _>>()
301 .map_err(|err| Error::Tls(format!("certificate: {err}")))?;
302 if chain.is_empty() {
303 return Err(Error::Tls("certificate chain holds no certificates".to_string()));
304 }
305 let key = PrivateKeyDer::from_pem_slice(identity.key()).map_err(|err| Error::Tls(format!("key: {err}")))?;
306 Ok((chain, key))
307}
308
309fn read_roots(paths: &[std::path::PathBuf]) -> Result<Vec<CertificateDer<'static>>, Error> {
315 let mut roots = Vec::new();
316 for path in paths {
317 let pem = std::fs::read(path).map_err(|err| Error::Tls(format!("{}: {err}", path.display())))?;
318 let certs = CertificateDer::pem_slice_iter(&pem)
319 .collect::<Result<Vec<_>, _>>()
320 .map_err(|err| Error::Tls(format!("{}: {err}", path.display())))?;
321 if certs.is_empty() {
322 return Err(Error::Tls(format!("{}: no certificates", path.display())));
323 }
324 roots.extend(certs);
325 }
326 Ok(roots)
327}
328
329fn root_store(paths: &[std::path::PathBuf]) -> Result<rustls::RootCertStore, Error> {
331 let mut store = rustls::RootCertStore::empty();
332 for root in read_roots(paths)? {
333 store.add(root).map_err(|err| Error::Tls(err.to_string()))?;
334 }
335 Ok(store)
336}
337
338#[derive(Debug)]
341struct NoVerify(Arc<rustls::crypto::CryptoProvider>);
342
343impl rustls::client::danger::ServerCertVerifier for NoVerify {
344 fn verify_server_cert(
345 &self,
346 _end_entity: &CertificateDer<'_>,
347 _intermediates: &[CertificateDer<'_>],
348 _server_name: &rustls::pki_types::ServerName<'_>,
349 _ocsp: &[u8],
350 _now: rustls::pki_types::UnixTime,
351 ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
352 Ok(rustls::client::danger::ServerCertVerified::assertion())
353 }
354
355 fn verify_tls12_signature(
356 &self,
357 message: &[u8],
358 cert: &CertificateDer<'_>,
359 dss: &rustls::DigitallySignedStruct,
360 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
361 rustls::crypto::verify_tls12_signature(message, cert, dss, &self.0.signature_verification_algorithms)
362 }
363
364 fn verify_tls13_signature(
365 &self,
366 message: &[u8],
367 cert: &CertificateDer<'_>,
368 dss: &rustls::DigitallySignedStruct,
369 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
370 rustls::crypto::verify_tls13_signature(message, cert, dss, &self.0.signature_verification_algorithms)
371 }
372
373 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
374 self.0.signature_verification_algorithms.supported_schemes()
375 }
376}