iroh_relay/protos/
common.rs

1//! Common types between the [`super::handshake`] and [`super::relay`] protocols.
2//!
3//! Hosts the [`FrameType`] enum to make sure we're not accidentally reusing frame type
4//! integers for different frames.
5
6use bytes::{Buf, BufMut};
7use n0_error::{e, stack_error};
8use noq_proto::{
9    VarInt,
10    coding::{Decodable, Encodable, UnexpectedEnd},
11};
12
13/// Possible frame types during handshaking
14#[repr(u32)]
15#[derive(Copy, Clone, PartialEq, Eq, Debug, num_enum::IntoPrimitive, strum::FromRepr)]
16// needs to be pub due to being exposed in error types
17#[non_exhaustive]
18pub enum FrameType {
19    /// The server frame type for the challenge response
20    ServerChallenge = 0,
21    /// The client frame type for the authentication frame
22    ClientAuth = 1,
23    /// The server frame type for authentication confirmation
24    ServerConfirmsAuth = 2,
25    /// The server frame type for authentication denial
26    ServerDeniesAuth = 3,
27    /// 32B dest pub key + ECN bytes + one datagram's content
28    ClientToRelayDatagram = 4,
29    /// 32B dest pub key + ECN byte + segment size u16 + datagrams contents
30    ClientToRelayDatagramBatch = 5,
31    /// 32B src pub key + ECN bytes + one datagram's content
32    RelayToClientDatagram = 6,
33    /// 32B src pub key + ECN byte + segment size u16 + datagrams contents
34    RelayToClientDatagramBatch = 7,
35    /// Sent from server to client to signal that a previous sender is no longer connected.
36    ///
37    /// That is, if A sent to B, and then if A disconnects, the server sends `FrameType::PeerGone`
38    /// to B so B can forget that a reverse path exists on that connection to get back to A
39    ///
40    /// 32B pub key of peer that's gone
41    EndpointGone = 8,
42    /// 8 byte ping payload, to be echoed back in a [`FrameType::Pong`].
43    Ping = 9,
44    /// 8 byte payload, the contents of ping being replied to
45    Pong = 10,
46    /// REMOVED since relay-protocol-v2, use `Self::Status` instead.
47    ///
48    /// Sent from server to client to tell the client if their connection is unhealthy somehow.
49    /// Contains only UTF-8 bytes.
50    Health = 11,
51
52    /// Sent from server to client for the server to declare that it's restarting.
53    /// Payload is two big endian u32 durations in milliseconds: when to reconnect,
54    /// and how long to try total.
55    Restarting = 12,
56
57    /// Sent from server to client to declare the connection health state.
58    ///
59    /// Added in `iroh-relay-v2` protocol. May not be sent to `iroh-relay-v1` clients.
60    ///
61    /// Uses a binary-encoded [`Status`] payload.
62    ///
63    /// [`Status`]: super::relay::Status
64    Status = 13,
65}
66
67#[stack_error(derive, add_meta)]
68#[allow(missing_docs)]
69#[non_exhaustive]
70pub enum FrameTypeError {
71    #[error("not enough bytes to parse frame type")]
72    UnexpectedEnd {
73        #[error(std_err)]
74        source: UnexpectedEnd,
75    },
76    #[error("frame type unknown")]
77    UnknownFrameType { tag: VarInt },
78}
79
80impl FrameType {
81    /// Writes the frame type to the buffer (as a QUIC-encoded varint).
82    pub(crate) fn write_to<O: BufMut>(&self, mut dst: O) -> O {
83        VarInt::from(*self).encode(&mut dst);
84        dst
85    }
86
87    /// Returns the amount of bytes that [`Self::write_to`] would write.
88    pub(crate) fn encoded_len(&self) -> usize {
89        // Copied implementation from `VarInt::size`
90        let x: u32 = (*self).into();
91        if x < 2u32.pow(6) {
92            1 // this will pretty much always be the case
93        } else if x < 2u32.pow(14) {
94            2
95        } else if x < 2u32.pow(30) {
96            4
97        } else {
98            unreachable!("Impossible FrameType primitive representation")
99        }
100    }
101
102    /// Parses the frame type (as a QUIC-encoded varint) from the first couple of bytes given
103    /// and returns the frame type and the rest.
104    pub(crate) fn from_bytes(buf: &mut impl Buf) -> Result<Self, FrameTypeError> {
105        let tag = VarInt::decode(buf).map_err(|err| e!(FrameTypeError::UnexpectedEnd, err))?;
106        let tag_u32 = u32::try_from(u64::from(tag))
107            .map_err(|_| e!(FrameTypeError::UnknownFrameType { tag }))?;
108        let frame_type = FrameType::from_repr(tag_u32)
109            .ok_or_else(|| e!(FrameTypeError::UnknownFrameType { tag }))?;
110        Ok(frame_type)
111    }
112}
113
114impl From<FrameType> for VarInt {
115    fn from(value: FrameType) -> Self {
116        (value as u32).into()
117    }
118}