Skip to main content

gbp_transport/
lib.rs

1//! Transport adapters for the Group Protocol Stack.
2//!
3//! Two transports are available:
4//! - **TCP** (this module): length-prefix framing over `TcpStream`.
5//! - **QUIC** ([`quic`]): the same framing over `quinn` streams.
6//!
7//! Both use identical wire format (`u32-LE length || CBOR bytes`), so a node
8//! can switch transports without any change to upper protocol layers.
9
10#![deny(missing_docs)]
11
12/// QUIC transport backed by quinn.
13pub mod quic;
14
15use gbp::{CodecError, GbpFrame};
16use tokio::io::{AsyncReadExt, AsyncWriteExt};
17use tokio::net::TcpStream;
18
19/// Maximum size of a single on-the-wire message (1 MiB).
20pub const MAX_FRAME: usize = 1 << 20;
21
22/// Errors raised by the transport layer.
23#[derive(Debug, thiserror::Error)]
24pub enum WireError {
25    /// Underlying I/O error.
26    #[error("io: {0}")]
27    Io(#[from] std::io::Error),
28    /// GBP base-layer codec error.
29    #[error("codec: {0}")]
30    Codec(#[from] CodecError),
31    /// Message exceeds [`MAX_FRAME`].
32    #[error("frame too large: {size} bytes (max {max})")]
33    TooLarge {
34        /// Observed size.
35        size: usize,
36        /// Configured limit.
37        max: usize,
38    },
39    /// QUIC connection or stream error.
40    #[error("quic: {0}")]
41    Quic(String),
42}
43
44impl WireError {
45    fn too_large(n: usize) -> Self {
46        Self::TooLarge {
47            size: n,
48            max: MAX_FRAME,
49        }
50    }
51}
52
53/// Writes a [`GbpFrame`] using `CBOR + length-prefix` framing.
54pub async fn write_frame(stream: &mut TcpStream, frame: &GbpFrame) -> Result<(), WireError> {
55    let bytes = frame.to_cbor();
56    write_blob(stream, &bytes).await
57}
58
59/// Reads a [`GbpFrame`] using `length-prefix + CBOR` framing.
60pub async fn read_frame(stream: &mut TcpStream) -> Result<GbpFrame, WireError> {
61    let buf = read_blob(stream).await?;
62    Ok(GbpFrame::from_cbor(&buf)?)
63}
64
65/// Writes an opaque length-prefixed blob (e.g. a serialised MLS Welcome or
66/// KeyPackage).
67pub async fn write_blob(stream: &mut TcpStream, data: &[u8]) -> Result<(), WireError> {
68    if data.len() > MAX_FRAME {
69        return Err(WireError::too_large(data.len()));
70    }
71    let len = (data.len() as u32).to_le_bytes();
72    stream.write_all(&len).await?;
73    stream.write_all(data).await?;
74    stream.flush().await?;
75    Ok(())
76}
77
78/// Reads an opaque length-prefixed blob.
79pub async fn read_blob(stream: &mut TcpStream) -> Result<Vec<u8>, WireError> {
80    let mut len_buf = [0u8; 4];
81    stream.read_exact(&mut len_buf).await?;
82    let len = u32::from_le_bytes(len_buf) as usize;
83    if len > MAX_FRAME {
84        return Err(WireError::too_large(len));
85    }
86    let mut buf = vec![0u8; len];
87    stream.read_exact(&mut buf).await?;
88    Ok(buf)
89}