enclavia_protocol/
egress.rs1use std::io;
15use std::net::Ipv4Addr;
16
17use serde::{Deserialize, Serialize};
18use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
19
20pub const MAX_OPEN_FRAME_SIZE: u32 = 4096;
24
25#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
34#[serde(tag = "type")]
35pub enum Open {
36 Tcp {
37 #[serde(with = "ipv4_octets")]
38 host: Ipv4Addr,
39 port: u16,
40 },
41}
42
43pub(crate) mod ipv4_octets {
48 use std::net::Ipv4Addr;
49
50 use serde::{Deserialize, Deserializer, Serialize, Serializer};
51
52 pub fn serialize<S: Serializer>(addr: &Ipv4Addr, s: S) -> Result<S::Ok, S::Error> {
53 addr.octets().serialize(s)
54 }
55
56 pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Ipv4Addr, D::Error> {
57 let bytes = <[u8; 4]>::deserialize(d)?;
58 Ok(Ipv4Addr::from(bytes))
59 }
60}
61
62#[derive(Debug, thiserror::Error)]
64pub enum ReadOpenError {
65 #[error("io error: {0}")]
66 Io(#[from] io::Error),
67 #[error("opener frame too large: {0} > {MAX_OPEN_FRAME_SIZE}")]
68 FrameTooLarge(u32),
69 #[error("failed to decode opener frame: {0}")]
70 Decode(#[from] ciborium::de::Error<io::Error>),
71}
72
73pub async fn read_open_frame<S>(stream: &mut S) -> Result<Open, ReadOpenError>
75where
76 S: AsyncRead + Unpin,
77{
78 let len = stream.read_u32().await?;
79 if len > MAX_OPEN_FRAME_SIZE {
80 return Err(ReadOpenError::FrameTooLarge(len));
81 }
82 let mut buf = vec![0u8; len as usize];
83 stream.read_exact(&mut buf).await?;
84 let open: Open = ciborium::from_reader(&buf[..])?;
85 Ok(open)
86}
87
88pub async fn write_open_frame<S>(stream: &mut S, open: &Open) -> io::Result<()>
90where
91 S: AsyncWrite + Unpin,
92{
93 let mut buf = Vec::new();
94 ciborium::into_writer(open, &mut buf).expect("ciborium encode Open frame");
95 let len: u32 = buf
96 .len()
97 .try_into()
98 .expect("Open frame fits in u32 (CBOR encoding is small)");
99 stream.write_all(&len.to_be_bytes()).await?;
100 stream.write_all(&buf).await?;
101 stream.flush().await?;
102 Ok(())
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn open_roundtrip_cbor() {
111 let open = Open::Tcp {
112 host: Ipv4Addr::new(127, 0, 0, 1),
113 port: 4242,
114 };
115 let mut buf = Vec::new();
116 ciborium::into_writer(&open, &mut buf).unwrap();
117 let decoded: Open = ciborium::from_reader(&buf[..]).unwrap();
118 assert_eq!(open, decoded);
119 }
120}