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