1use std::net::SocketAddr;
18use std::sync::Arc;
19use std::sync::Once;
20
21use async_trait::async_trait;
22use quinn::crypto::rustls::{QuicClientConfig, QuicServerConfig};
23use quinn::{ClientConfig, Connection, Endpoint, RecvStream, SendStream, ServerConfig};
24use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer, ServerName, UnixTime};
25
26use crate::mesh::{MAX_MESSAGE_SIZE, MeshError, MeshStream, MeshTransport, TransportListener};
27
28const MESH_ALPN: &[u8] = b"weftos-mesh";
30
31const MESH_SNI: &str = "localhost";
33
34static INSTALL_CRYPTO: Once = Once::new();
35
36fn ensure_crypto_provider() {
37 INSTALL_CRYPTO.call_once(|| {
38 let _ = rustls::crypto::ring::default_provider().install_default();
40 });
41}
42
43#[derive(Debug)]
46struct SkipServerVerification(Arc<rustls::crypto::CryptoProvider>);
47
48impl SkipServerVerification {
49 fn new() -> Arc<Self> {
50 Arc::new(Self(Arc::new(rustls::crypto::ring::default_provider())))
51 }
52}
53
54impl rustls::client::danger::ServerCertVerifier for SkipServerVerification {
55 fn verify_server_cert(
56 &self,
57 _end_entity: &CertificateDer<'_>,
58 _intermediates: &[CertificateDer<'_>],
59 _server_name: &ServerName<'_>,
60 _ocsp_response: &[u8],
61 _now: UnixTime,
62 ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
63 Ok(rustls::client::danger::ServerCertVerified::assertion())
64 }
65
66 fn verify_tls12_signature(
67 &self,
68 message: &[u8],
69 cert: &CertificateDer<'_>,
70 dss: &rustls::DigitallySignedStruct,
71 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
72 rustls::crypto::verify_tls12_signature(
73 message,
74 cert,
75 dss,
76 &self.0.signature_verification_algorithms,
77 )
78 }
79
80 fn verify_tls13_signature(
81 &self,
82 message: &[u8],
83 cert: &CertificateDer<'_>,
84 dss: &rustls::DigitallySignedStruct,
85 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
86 rustls::crypto::verify_tls13_signature(
87 message,
88 cert,
89 dss,
90 &self.0.signature_verification_algorithms,
91 )
92 }
93
94 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
95 self.0.signature_verification_algorithms.supported_schemes()
96 }
97}
98
99fn make_server_config() -> Result<ServerConfig, MeshError> {
102 ensure_crypto_provider();
103
104 let certified = rcgen::generate_simple_self_signed(vec![MESH_SNI.into()]).map_err(|e| {
105 MeshError::Transport(format!("quic self-signed cert: {e}"))
106 })?;
107 let cert_der = CertificateDer::from(certified.cert);
108 let key_der = PrivatePkcs8KeyDer::from(certified.key_pair.serialize_der());
109
110 let mut tls = rustls::ServerConfig::builder()
111 .with_no_client_auth()
112 .with_single_cert(vec![cert_der], PrivateKeyDer::Pkcs8(key_der))
113 .map_err(|e| MeshError::Transport(format!("quic server tls: {e}")))?;
114 tls.alpn_protocols = vec![MESH_ALPN.to_vec()];
115
116 let quic_tls = QuicServerConfig::try_from(tls)
117 .map_err(|e| MeshError::Transport(format!("quic server crypto: {e}")))?;
118 Ok(ServerConfig::with_crypto(Arc::new(quic_tls)))
119}
120
121fn make_client_config() -> Result<ClientConfig, MeshError> {
122 ensure_crypto_provider();
123
124 let mut tls = rustls::ClientConfig::builder()
125 .dangerous()
126 .with_custom_certificate_verifier(SkipServerVerification::new())
127 .with_no_client_auth();
128 tls.alpn_protocols = vec![MESH_ALPN.to_vec()];
129
130 let quic_tls = QuicClientConfig::try_from(tls)
131 .map_err(|e| MeshError::Transport(format!("quic client crypto: {e}")))?;
132 Ok(ClientConfig::new(Arc::new(quic_tls)))
133}
134
135fn strip_quic_scheme(addr: &str) -> &str {
136 addr.strip_prefix("quic://").unwrap_or(addr)
137}
138
139fn parse_socket_addr(addr: &str) -> Result<SocketAddr, MeshError> {
140 strip_quic_scheme(addr)
141 .parse()
142 .map_err(|e: std::net::AddrParseError| MeshError::Io(format!("bad quic addr '{addr}': {e}")))
143}
144
145pub struct QuicMeshStream {
156 send: SendStream,
157 recv: RecvStream,
158 remote: SocketAddr,
159 _conn: Connection,
161 _endpoint: Option<Endpoint>,
163 len_buf: [u8; 4],
165 len_filled: usize,
166 body: Option<Vec<u8>>,
167 body_filled: usize,
168}
169
170impl QuicMeshStream {
171 fn new(
172 send: SendStream,
173 recv: RecvStream,
174 remote: SocketAddr,
175 conn: Connection,
176 endpoint: Option<Endpoint>,
177 ) -> Self {
178 Self {
179 send,
180 recv,
181 remote,
182 _conn: conn,
183 _endpoint: endpoint,
184 len_buf: [0u8; 4],
185 len_filled: 0,
186 body: None,
187 body_filled: 0,
188 }
189 }
190
191 fn reset_frame(&mut self) {
192 self.len_filled = 0;
193 self.body = None;
194 self.body_filled = 0;
195 }
196}
197
198#[async_trait]
199impl MeshStream for QuicMeshStream {
200 async fn send(&mut self, data: &[u8]) -> Result<(), MeshError> {
201 if data.len() > MAX_MESSAGE_SIZE {
202 return Err(MeshError::MessageTooLarge {
203 size: data.len(),
204 max: MAX_MESSAGE_SIZE,
205 });
206 }
207 let len = (data.len() as u32).to_be_bytes();
208 self.send
209 .write_all(&len)
210 .await
211 .map_err(|e| MeshError::Io(e.to_string()))?;
212 self.send
213 .write_all(data)
214 .await
215 .map_err(|e| MeshError::Io(e.to_string()))?;
216 Ok(())
219 }
220
221 async fn recv(&mut self) -> Result<Vec<u8>, MeshError> {
222 while self.len_filled < 4 {
227 let Self {
228 recv,
229 len_buf,
230 len_filled,
231 ..
232 } = self;
233 let n = recv
234 .read(&mut len_buf[*len_filled..])
235 .await
236 .map_err(|e| MeshError::Io(e.to_string()))?
237 .ok_or(MeshError::ConnectionClosed)?;
238 *len_filled += n;
239 }
240
241 if self.body.is_none() {
242 let len = u32::from_be_bytes(self.len_buf) as usize;
243 if len > MAX_MESSAGE_SIZE {
244 self.reset_frame();
245 return Err(MeshError::MessageTooLarge {
246 size: len,
247 max: MAX_MESSAGE_SIZE,
248 });
249 }
250 self.body = Some(vec![0u8; len]);
251 self.body_filled = 0;
252 }
253
254 let expected = self.body.as_ref().map(|b| b.len()).unwrap_or(0);
255 while self.body_filled < expected {
256 let Self {
257 recv,
258 body,
259 body_filled,
260 ..
261 } = self;
262 let buf = body
263 .as_mut()
264 .expect("body allocated before body-fill loop");
265 if expected == 0 {
266 break;
267 }
268 let n = recv
269 .read(&mut buf[*body_filled..])
270 .await
271 .map_err(|e| MeshError::Io(e.to_string()))?
272 .ok_or(MeshError::ConnectionClosed)?;
273 *body_filled += n;
274 }
275
276 let out = self.body.take().expect("body complete");
277 self.reset_frame();
278 Ok(out)
279 }
280
281 async fn close(&mut self) -> Result<(), MeshError> {
282 self.send
284 .finish()
285 .map_err(|e| MeshError::Io(e.to_string()))?;
286 Ok(())
287 }
288
289 fn remote_addr(&self) -> Option<SocketAddr> {
290 Some(self.remote)
291 }
292}
293
294pub struct QuicTransportListener {
298 endpoint: Endpoint,
299}
300
301#[async_trait]
302impl TransportListener for QuicTransportListener {
303 async fn accept(&mut self) -> Result<(Box<dyn MeshStream>, SocketAddr), MeshError> {
304 let incoming = self
305 .endpoint
306 .accept()
307 .await
308 .ok_or_else(|| MeshError::Io("quic endpoint closed".into()))?;
309 let conn = incoming
310 .await
311 .map_err(|e| MeshError::Io(format!("quic accept handshake: {e}")))?;
312 let remote = conn.remote_address();
313 let (send, recv) = conn
314 .accept_bi()
315 .await
316 .map_err(|e| MeshError::Io(format!("quic accept_bi: {e}")))?;
317 Ok((
318 Box::new(QuicMeshStream::new(send, recv, remote, conn, None)),
319 remote,
320 ))
321 }
322
323 fn local_addr(&self) -> Result<SocketAddr, MeshError> {
324 self.endpoint
325 .local_addr()
326 .map_err(|e| MeshError::Io(e.to_string()))
327 }
328}
329
330pub struct QuicTransport;
338
339#[async_trait]
340impl MeshTransport for QuicTransport {
341 fn name(&self) -> &str {
342 "quic"
343 }
344
345 async fn listen(&self, addr: &str) -> Result<Box<dyn TransportListener>, MeshError> {
346 let bind_addr = parse_socket_addr(addr)?;
347 let server_config = make_server_config()?;
348 let endpoint = Endpoint::server(server_config, bind_addr)
349 .map_err(|e| MeshError::Io(format!("quic bind {bind_addr}: {e}")))?;
350 Ok(Box::new(QuicTransportListener { endpoint }))
351 }
352
353 async fn connect(&self, addr: &str) -> Result<Box<dyn MeshStream>, MeshError> {
354 let remote = parse_socket_addr(addr)?;
355 let client_config = make_client_config()?;
356
357 let bind: SocketAddr = if remote.is_ipv6() {
359 "[::]:0".parse().unwrap()
360 } else {
361 "0.0.0.0:0".parse().unwrap()
362 };
363 let mut endpoint =
364 Endpoint::client(bind).map_err(|e| MeshError::Io(format!("quic client endpoint: {e}")))?;
365 endpoint.set_default_client_config(client_config);
366
367 let conn = endpoint
368 .connect(remote, MESH_SNI)
369 .map_err(|e| MeshError::Io(format!("quic connect setup: {e}")))?
370 .await
371 .map_err(|e| MeshError::Io(format!("quic connect: {e}")))?;
372
373 let (send, recv) = conn
374 .open_bi()
375 .await
376 .map_err(|e| MeshError::Io(format!("quic open_bi: {e}")))?;
377
378 Ok(Box::new(QuicMeshStream::new(
379 send,
380 recv,
381 remote,
382 conn,
383 Some(endpoint),
384 )))
385 }
386
387 fn supports(&self, addr: &str) -> bool {
388 addr.starts_with("quic://")
389 }
390}
391
392#[cfg(test)]
395mod tests {
396 use super::*;
397
398 #[tokio::test]
399 async fn quic_transport_connect_send_recv() {
400 let transport = QuicTransport;
401
402 let mut listener = transport.listen("127.0.0.1:0").await.unwrap();
403 let addr = listener.local_addr().unwrap();
404 let quic_url = format!("quic://{addr}");
405
406 let send_task = tokio::spawn(async move {
407 let mut stream = QuicTransport.connect(&quic_url).await.unwrap();
408 stream.send(b"hello from node A").await.unwrap();
409 let response = stream.recv().await.unwrap();
410 assert_eq!(response, b"hello from node B");
411 stream.close().await.unwrap();
412 });
413
414 let (mut server_stream, _client_addr) = listener.accept().await.unwrap();
415 let received = server_stream.recv().await.unwrap();
416 assert_eq!(received, b"hello from node A");
417 server_stream.send(b"hello from node B").await.unwrap();
418
419 send_task.await.unwrap();
420 }
421
422 #[tokio::test]
424 async fn two_node_cluster_forms_over_quic() {
425 use crate::mesh::WeftHandshake;
426 use crate::mesh_framing::{FrameType, MeshFrame};
427
428 let transport = QuicTransport;
429 let mut listener = transport.listen("127.0.0.1:0").await.unwrap();
430 let addr = listener.local_addr().unwrap();
431 let quic_url = format!("quic://{addr}");
432
433 let node_a = tokio::spawn(async move {
434 let mut stream = QuicTransport.connect(&quic_url).await.unwrap();
435
436 let handshake = WeftHandshake {
437 node_id: "node-a".into(),
438 governance_genesis_hash: [0u8; 32],
439 governance_version: "1.0".into(),
440 capabilities: 0xFF,
441 kem_supported: false,
442 chain_seq: 0,
443 supported_sync_streams: vec![1, 2, 3],
444 };
445 let payload = serde_json::to_vec(&handshake).unwrap();
446 let frame = MeshFrame {
447 frame_type: FrameType::Handshake,
448 payload,
449 };
450 stream.send(&frame.encode().unwrap()).await.unwrap();
451
452 let data = stream.recv().await.unwrap();
453 let len = u32::from_be_bytes([data[0], data[1], data[2], data[3]]) as usize;
454 let response = MeshFrame::decode(&data[4..4 + len]).unwrap();
455 assert_eq!(response.frame_type, FrameType::Handshake);
456 let remote: WeftHandshake = serde_json::from_slice(&response.payload).unwrap();
457 assert_eq!(remote.node_id, "node-b");
458
459 stream.close().await.unwrap();
460 });
461
462 let (mut stream, _) = listener.accept().await.unwrap();
463 let data = stream.recv().await.unwrap();
464 let len = u32::from_be_bytes([data[0], data[1], data[2], data[3]]) as usize;
465 let frame = MeshFrame::decode(&data[4..4 + len]).unwrap();
466 assert_eq!(frame.frame_type, FrameType::Handshake);
467 let remote: WeftHandshake = serde_json::from_slice(&frame.payload).unwrap();
468 assert_eq!(remote.node_id, "node-a");
469
470 let handshake = WeftHandshake {
471 node_id: "node-b".into(),
472 governance_genesis_hash: [0u8; 32],
473 governance_version: "1.0".into(),
474 capabilities: 0xFF,
475 kem_supported: false,
476 chain_seq: 0,
477 supported_sync_streams: vec![1, 2, 3],
478 };
479 let payload = serde_json::to_vec(&handshake).unwrap();
480 let response = MeshFrame {
481 frame_type: FrameType::Handshake,
482 payload,
483 };
484 stream.send(&response.encode().unwrap()).await.unwrap();
485
486 node_a.await.unwrap();
487 }
488
489 #[tokio::test]
491 async fn two_node_quic_with_noise() {
492 use crate::mesh_noise::{EncryptedChannel, NoiseChannel, NoiseConfig, NoisePattern};
493 use rand::RngCore;
494
495 let transport = QuicTransport;
496 let mut listener = transport.listen("127.0.0.1:0").await.unwrap();
497 let addr = listener.local_addr().unwrap();
498 let quic_url = format!("quic://{addr}");
499
500 let mut key_a = [0u8; 32];
501 let mut key_b = [0u8; 32];
502 rand::thread_rng().fill_bytes(&mut key_a);
503 rand::thread_rng().fill_bytes(&mut key_b);
504
505 let cfg_a = NoiseConfig {
506 pattern: NoisePattern::XX,
507 local_private_key: key_a,
508 remote_static_key: None,
509 };
510 let cfg_b = NoiseConfig {
511 pattern: NoisePattern::XX,
512 local_private_key: key_b,
513 remote_static_key: None,
514 };
515
516 let node_a = tokio::spawn(async move {
517 let stream = QuicTransport.connect(&quic_url).await.unwrap();
518 let mut ch = NoiseChannel::initiate(stream, &cfg_a).await.unwrap();
519 ch.send_encrypted(b"secret-from-a").await.unwrap();
520 let reply = ch.recv_encrypted().await.unwrap();
521 assert_eq!(reply, b"secret-from-b");
522 ch.close().await.unwrap();
523 });
524
525 let (stream, _) = listener.accept().await.unwrap();
526 let mut ch = NoiseChannel::respond(stream, &cfg_b).await.unwrap();
527 let msg = ch.recv_encrypted().await.unwrap();
528 assert_eq!(msg, b"secret-from-a");
529 ch.send_encrypted(b"secret-from-b").await.unwrap();
530
531 node_a.await.unwrap();
532 }
533
534 #[test]
535 fn quic_transport_supports() {
536 let t = QuicTransport;
537 assert!(t.supports("quic://127.0.0.1:9489"));
538 assert!(!t.supports("tcp://127.0.0.1:9489"));
539 assert!(!t.supports("ws://127.0.0.1:9489"));
540 assert!(!t.supports("127.0.0.1:9489"));
541 }
542
543 #[tokio::test]
544 async fn quic_recv_survives_select_cancellation() {
545 use std::time::Duration;
546
547 let transport = QuicTransport;
548 let mut listener = transport.listen("127.0.0.1:0").await.unwrap();
549 let addr = listener.local_addr().unwrap();
550 let quic_url = format!("quic://{addr}");
551
552 let payload_a = vec![0xAAu8; 64];
553 let payload_b = vec![0xBBu8; 128];
554 let payload_a2 = payload_a.clone();
555 let payload_b2 = payload_b.clone();
556
557 let writer = tokio::spawn(async move {
558 let mut stream = QuicTransport.connect(&quic_url).await.unwrap();
559 stream.send(&payload_a2).await.unwrap();
560 tokio::time::sleep(Duration::from_millis(5)).await;
561 stream.send(&payload_b2).await.unwrap();
562 tokio::time::sleep(Duration::from_millis(500)).await;
566 let _ = stream.close().await;
567 });
568
569 let (mut server, _) = listener.accept().await.unwrap();
570
571 let mut got_first = None;
572 for _ in 0..200 {
573 tokio::select! {
574 biased;
575 result = server.recv() => {
576 got_first = Some(result.expect("recv after cancel must succeed"));
577 break;
578 }
579 _ = tokio::time::sleep(Duration::from_micros(50)) => {}
580 }
581 }
582 let first = got_first.expect("should eventually complete first frame");
583 assert_eq!(first, payload_a);
584
585 let second = server.recv().await.expect("second frame");
586 assert_eq!(second, payload_b);
587
588 writer.await.unwrap();
589 }
590}