1//! Helpers to manage lists of extensions within relay messages.
2//!
3//! These are used widely throughout the HS code,
4//! but also in the ntor-v3 handshake.
56use derive_deftly::Deftly;
7use tor_bytes::{EncodeError, EncodeResult, Readable, Reader, Result, Writeable, Writer};
8use tor_memquota::{derive_deftly_template_HasMemoryCost, HasMemoryCostStructural};
910/// A list of extensions, represented in a common format used by many messages.
11///
12/// The common format is:
13/// ```text
14/// N_EXTENSIONS [1 byte]
15/// N_EXTENSIONS times:
16/// EXT_FIELD_TYPE [1 byte]
17/// EXT_FIELD_LEN [1 byte]
18/// EXT_FIELD [EXT_FIELD_LEN bytes]
19/// ```
20///
21/// It is subject to the additional restraints:
22///
23/// * Each extension type SHOULD be sent only once in a message.
24/// * Parties MUST ignore any occurrences all occurrences of an extension
25/// with a given type after the first such occurrence.
26/// * Extensions SHOULD be sent in numerically ascending order by type.
27#[derive(Clone, Debug, derive_more::Deref, derive_more::DerefMut, Deftly)]
28#[derive_deftly(HasMemoryCost)]
29#[deftly(has_memory_cost(bounds = "T: HasMemoryCostStructural"))]
30pub(super) struct ExtList<T> {
31/// The extensions themselves.
32extensions: Vec<T>,
33}
34impl<T> Default for ExtList<T> {
35fn default() -> Self {
36Self {
37 extensions: Vec::new(),
38 }
39 }
40}
4142/// As ExtList, but held by reference.
43#[derive(Clone, Debug, derive_more::Deref, derive_more::DerefMut, derive_more::From)]
44pub(super) struct ExtListRef<'a, T> {
45/// A reference to a slice of extensions.
46extensions: &'a [T],
47}
4849/// A kind of extension that can be used with some kind of relay message.
50///
51/// Each extendible message will likely define its own enum,
52/// implementing this trait,
53/// representing the possible extensions.
54pub(super) trait ExtGroup: Readable + Writeable {
55/// An identifier kind used with this sort of extension
56type Id: From<u8> + Into<u8> + Eq + PartialEq + Ord + Copy;
57/// The field-type id for this particular extension.
58fn type_id(&self) -> Self::Id;
59}
60/// A single typed extension that can be used with some kind of relay message.
61pub(super) trait Ext: Sized {
62/// An identifier kind used with this sort of extension.
63 ///
64 /// Typically defined with caret_int.
65type Id: From<u8> + Into<u8>;
66/// The field-type id for this particular extension.
67fn type_id(&self) -> Self::Id;
68/// Extract the body (not the type or the length) from a single
69 /// extension.
70fn take_body_from(b: &mut Reader<'_>) -> Result<Self>;
71/// Write the body (not the type or the length) for a single extension.
72fn write_body_onto<B: Writer + ?Sized>(&self, b: &mut B) -> EncodeResult<()>;
73}
74impl<T: ExtGroup> Readable for ExtList<T> {
75fn take_from(b: &mut Reader<'_>) -> Result<Self> {
76let n_extensions = b.take_u8()?;
77let extensions: Result<Vec<T>> = (0..n_extensions).map(|_| b.extract::<T>()).collect();
78Ok(Self {
79 extensions: extensions?,
80 })
81 }
82}
83impl<'a, T: ExtGroup> Writeable for ExtListRef<'a, T> {
84fn write_onto<B: Writer + ?Sized>(&self, b: &mut B) -> EncodeResult<()> {
85let n_extensions = self
86.extensions
87 .len()
88 .try_into()
89 .map_err(|_| EncodeError::BadLengthValue)?;
90 b.write_u8(n_extensions);
91let mut exts_sorted: Vec<&T> = self.extensions.iter().collect();
92 exts_sorted.sort_by_key(|ext| ext.type_id());
93 exts_sorted.iter().try_for_each(|ext| ext.write_onto(b))?;
94Ok(())
95 }
96}
97impl<T: ExtGroup> Writeable for ExtList<T> {
98fn write_onto<B: Writer + ?Sized>(&self, b: &mut B) -> EncodeResult<()> {
99 ExtListRef::from(&self.extensions[..]).write_onto(b)
100 }
101}
102impl<T: ExtGroup> ExtList<T> {
103/// Insert `ext` into this list of extensions, replacing any previous
104 /// extension with the same field type ID.
105pub(super) fn replace_by_type(&mut self, ext: T) {
106self.retain(|e| e.type_id() != ext.type_id());
107self.push(ext);
108 }
109/// Consume this ExtList and return its members as a vector.
110pub(super) fn into_vec(self) -> Vec<T> {
111self.extensions
112 }
113}
114115/// An unrecognized or unencoded extension for some relay message.
116#[derive(Clone, Debug, Deftly, Eq, PartialEq)]
117#[derive_deftly(HasMemoryCost)]
118// Use `Copy + 'static` and `#[deftly(has_memory_cost(copy))]` so that we don't
119// need to derive HasMemoryCost for the id types, which are indeed all Copy.
120#[deftly(has_memory_cost(bounds = "ID: Copy + 'static"))]
121pub struct UnrecognizedExt<ID> {
122/// The field type ID for this extension.
123#[deftly(has_memory_cost(copy))]
124pub(super) type_id: ID,
125/// The body of this extension.
126pub(super) body: Vec<u8>,
127}
128129impl<ID> UnrecognizedExt<ID> {
130/// Return a new unrecognized extension with a given ID and body.
131 ///
132 /// NOTE: nothing actually enforces that this type ID is not
133 /// recognized.
134 ///
135 /// NOTE: This function accepts bodies longer than 255 bytes, but
136 /// it is not possible to encode them.
137pub fn new(type_id: ID, body: impl Into<Vec<u8>>) -> Self {
138Self {
139 type_id,
140 body: body.into(),
141 }
142 }
143}
144145/// Declare an Extension group that takes a given identifier.
146//
147// TODO: This is rather similar to restrict_msg(), isn't it? Also, We use this
148// pattern of (number, (cmd, length, body)*) a few of times in Tor outside the relaycell
149// module. Perhaps we can extend and unify our code here...
150macro_rules! decl_extension_group {
151 {
152 $( #[$meta:meta] )*
153$v:vis enum $id:ident [ $type_id:ty ] {
154 $(
155 $(#[$cmeta:meta])*
156 $([feature: #[$fmeta:meta]])?
157$case:ident),*
158 $(,)?
159}
160 } => {paste::paste!{
161 $( #[$meta] )*
162$v enum $id {
163 $( $(#[$cmeta])*
164 $( #[$fmeta] )?
165$case($case),
166 )*
167/// An extension of a type we do not recognize, or which we have not
168 /// encoded.
169Unrecognized(crate::relaycell::extlist::UnrecognizedExt<$type_id>)
170 }
171impl tor_bytes::Readable for $id {
172fn take_from(b: &mut Reader<'_>) -> tor_bytes::Result<Self> {
173#[allow(unused)]
174use crate::relaycell::extlist::Ext as _;
175let type_id = b.take_u8()?.into();
176Ok(match type_id {
177 $(
178 $( #[$fmeta] )?
179$type_id::[< $case:snake:upper >] => {
180Self::$case( b.read_nested_u8len(|r| $case::take_body_from(r))? )
181 }
182 )*
183_ => {
184Self::Unrecognized(crate::relaycell::extlist::UnrecognizedExt {
185 type_id,
186 body: b.read_nested_u8len(|r| Ok(r.take_rest().into()))?,
187 })
188 }
189 })
190 }
191 }
192impl tor_bytes::Writeable for $id {
193fn write_onto<B: Writer + ?Sized>(&self, b: &mut B) -> tor_bytes::EncodeResult<
194()> {
195#![allow(unused_imports)]
196use crate::relaycell::extlist::Ext as _;
197use tor_bytes::Writeable as _;
198use std::ops::DerefMut;
199match self {
200 $(
201 $( #[$fmeta] )?
202Self::$case(val) => {
203 b.write_u8(val.type_id().into());
204let mut nested = b.write_nested_u8len();
205 val.write_body_onto(nested.deref_mut())?;
206 nested.finish()?;
207 }
208 )*
209Self::Unrecognized(unrecognized) => {
210 b.write_u8(unrecognized.type_id.into());
211let mut nested = b.write_nested_u8len();
212 nested.write_all(&unrecognized.body[..]);
213 nested.finish()?;
214 }
215 }
216Ok(())
217 }
218 }
219impl crate::relaycell::extlist::ExtGroup for $id {
220type Id = $type_id;
221fn type_id(&self) -> Self::Id {
222#![allow(unused_imports)]
223use crate::relaycell::extlist::Ext as _;
224match self {
225 $(
226 $( #[$fmeta] )?
227Self::$case(val) => val.type_id(),
228 )*
229Self::Unrecognized(unrecognized) => unrecognized.type_id,
230 }
231 }
232 }
233 $(
234 $( #[$fmeta] )?
235impl From<$case> for $id {
236fn from(val: $case) -> $id {
237$id :: $case ( val )
238 }
239 }
240 )*
241impl From<crate::relaycell::extlist::UnrecognizedExt<$type_id>> for $id {
242fn from(val: crate::relaycell::extlist::UnrecognizedExt<$type_id>) -> $id {
243$id :: Unrecognized(val)
244 }
245 }
246}}
247}
248pub(super) use decl_extension_group;