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