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.
5

            
6
use derive_deftly::Deftly;
7
use tor_bytes::{EncodeError, EncodeResult, Readable, Reader, Result, Writeable, Writer};
8
use tor_memquota::{HasMemoryCostStructural, derive_deftly_template_HasMemoryCost};
9

            
10
/// 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"))]
30
pub(super) struct ExtList<T> {
31
    /// The extensions themselves.
32
    pub(super) extensions: Vec<T>,
33
}
34
impl<T> Default for ExtList<T> {
35
16695
    fn default() -> Self {
36
16695
        Self {
37
16695
            extensions: Vec::new(),
38
16695
        }
39
16695
    }
40
}
41

            
42
/// As ExtList, but held by reference.
43
#[derive(Clone, Debug, derive_more::Deref, derive_more::DerefMut, derive_more::From)]
44
pub(super) struct ExtListRef<'a, T> {
45
    /// A reference to a slice of extensions.
46
    extensions: &'a [T],
47
}
48

            
49
/// 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.
54
pub(super) trait ExtGroup: Readable + Writeable {
55
    /// An identifier kind used with this sort of extension
56
    type Id: From<u8> + Into<u8> + Eq + PartialEq + Ord + Copy;
57
    /// The field-type id for this particular extension.
58
    fn type_id(&self) -> Self::Id;
59
}
60
/// A single typed extension that can be used with some kind of relay message.
61
pub(super) trait Ext: Sized {
62
    /// An identifier kind used with this sort of extension.
63
    ///
64
    /// Typically defined with caret_int.
65
    type Id: From<u8> + Into<u8>;
66
    /// The field-type id for this particular extension.
67
    fn type_id(&self) -> Self::Id;
68
    /// Extract the body (not the type or the length) from a single
69
    /// extension.
70
    fn take_body_from(b: &mut Reader<'_>) -> Result<Self>;
71
    /// Write the body (not the type or the length) for a single extension.
72
    fn write_body_onto<B: Writer + ?Sized>(&self, b: &mut B) -> EncodeResult<()>;
73
}
74
impl<T: ExtGroup> Readable for ExtList<T> {
75
3528
    fn take_from(b: &mut Reader<'_>) -> Result<Self> {
76
3528
        let n_extensions = b.take_u8()?;
77
3528
        let extensions: Result<Vec<T>> = (0..n_extensions).map(|_| b.extract::<T>()).collect();
78
        Ok(Self {
79
3528
            extensions: extensions?,
80
        })
81
3528
    }
82
}
83
impl<'a, T: ExtGroup> Writeable for ExtListRef<'a, T> {
84
4635
    fn write_onto<B: Writer + ?Sized>(&self, b: &mut B) -> EncodeResult<()> {
85
4635
        let n_extensions = self
86
4635
            .extensions
87
4635
            .len()
88
4635
            .try_into()
89
4635
            .map_err(|_| EncodeError::BadLengthValue)?;
90
4635
        b.write_u8(n_extensions);
91
4635
        let mut exts_sorted: Vec<&T> = self.extensions.iter().collect();
92
4635
        exts_sorted.sort_by_key(|ext| ext.type_id());
93
4635
        exts_sorted.iter().try_for_each(|ext| ext.write_onto(b))?;
94
4635
        Ok(())
95
4635
    }
96
}
97
impl<T: ExtGroup> Writeable for ExtList<T> {
98
370
    fn write_onto<B: Writer + ?Sized>(&self, b: &mut B) -> EncodeResult<()> {
99
370
        ExtListRef::from(&self.extensions[..]).write_onto(b)
100
370
    }
101
}
102
impl<T: ExtGroup> ExtList<T> {
103
    /// Insert `ext` into this list of extensions, replacing any previous
104
    /// extension with the same field type ID.
105
    #[cfg(feature = "hs")] // currently, only used when "hs' is enabled.
106
189
    pub(super) fn replace_by_type(&mut self, ext: T) {
107
189
        self.retain(|e| e.type_id() != ext.type_id());
108
189
        self.push(ext);
109
189
    }
110
    /// Consume this ExtList and return its members as a vector.
111
2898
    pub(super) fn into_vec(self) -> Vec<T> {
112
2898
        self.extensions
113
2898
    }
114
}
115

            
116
/// An unrecognized or unencoded extension for some relay message.
117
#[derive(Clone, Debug, Deftly, Eq, PartialEq)]
118
#[derive_deftly(HasMemoryCost)]
119
// Use `Copy + 'static` and `#[deftly(has_memory_cost(copy))]` so that we don't
120
// need to derive HasMemoryCost for the id types, which are indeed all Copy.
121
#[deftly(has_memory_cost(bounds = "ID: Copy + 'static"))]
122
pub struct UnrecognizedExt<ID> {
123
    /// The field type ID for this extension.
124
    #[deftly(has_memory_cost(copy))]
125
    pub(super) type_id: ID,
126
    /// The body of this extension.
127
    pub(super) body: Vec<u8>,
128
}
129

            
130
impl<ID> UnrecognizedExt<ID> {
131
    /// Return a new unrecognized extension with a given ID and body.
132
    ///
133
    /// NOTE: nothing actually enforces that this type ID is not
134
    /// recognized.
135
    ///
136
    /// NOTE: This function accepts bodies longer than 255 bytes, but
137
    /// it is not possible to encode them.
138
2
    pub fn new(type_id: ID, body: impl Into<Vec<u8>>) -> Self {
139
2
        Self {
140
2
            type_id,
141
2
            body: body.into(),
142
2
        }
143
2
    }
144
}
145

            
146
/// Declare an Extension group that takes a given identifier.
147
//
148
// TODO: This is rather similar to restrict_msg(), isn't it?  Also, We use this
149
// pattern of (number, (cmd, length, body)*) a few of times in Tor outside the relaycell
150
// module.  Perhaps we can extend and unify our code here...
151
macro_rules! decl_extension_group {
152
    {
153
        $( #[$meta:meta] )*
154
        $v:vis enum $id:ident [ $type_id:ty ] {
155
            $(
156
                $(#[$cmeta:meta])*
157
                $([feature: #[$fmeta:meta]])?
158
                $case:ident),*
159
            $(,)?
160
        }
161
    } => {paste::paste!{
162
        $( #[$meta] )*
163
63
        $v enum $id {
164
63
            $( $(#[$cmeta])*
165
63
               $( #[$fmeta] )?
166
63
               $case($case),
167
63
            )*
168
63
            /// An extension of a type we do not recognize, or which we have not
169
63
            /// encoded.
170
63
            Unrecognized(crate::relaycell::extlist::UnrecognizedExt<$type_id>)
171
63
        }
172
63
        impl tor_bytes::Readable for $id {
173
1197
            fn take_from(b: &mut Reader<'_>) -> tor_bytes::Result<Self> {
174
63
                #[allow(unused)]
175
63
                use crate::relaycell::extlist::Ext as _;
176
1197
                let type_id = b.take_u8()?.into();
177
1197
                Ok(match type_id {
178
63
                    $(
179
63
                        $( #[$fmeta] )?
180
63
                        $type_id::[< $case:snake:upper >] => {
181
1024
                            Self::$case( b.read_nested_u8len(|r| $case::take_body_from(r))? )
182
63
                        }
183
63
                    )*
184
63
                    _ => {
185
63
                        Self::Unrecognized(crate::relaycell::extlist::UnrecognizedExt {
186
189
                            type_id,
187
192
                            body: b.read_nested_u8len(|r| Ok(r.take_rest().into()))?,
188
63
                        })
189
63
                    }
190
63
                })
191
1197
            }
192
63
        }
193
63
        impl tor_bytes::Writeable for $id {
194
4390
            fn write_onto<B: Writer + ?Sized>(&self, b: &mut B) -> tor_bytes::EncodeResult<
195
4389
()> {
196
63
                #![allow(unused_imports)]
197
63
                use crate::relaycell::extlist::Ext as _;
198
63
                use tor_bytes::Writeable as _;
199
63
                use std::ops::DerefMut;
200
4390
                match self {
201
63
                    $(
202
63
                        $( #[$fmeta] )?
203
4382
                        Self::$case(val) => {
204
4382
                            b.write_u8(val.type_id().into());
205
4382
                            let mut nested = b.write_nested_u8len();
206
4382
                            val.write_body_onto(nested.deref_mut())?;
207
4382
                            nested.finish()?;
208
63
                        }
209
63
                    )*
210
70
                    Self::Unrecognized(unrecognized) => {
211
70
                        b.write_u8(unrecognized.type_id.into());
212
70
                        let mut nested = b.write_nested_u8len();
213
70
                        nested.write_all(&unrecognized.body[..]);
214
70
                        nested.finish()?;
215
63
                    }
216
63
                }
217
4390
                Ok(())
218
4390
            }
219
63
        }
220
63
        impl crate::relaycell::extlist::ExtGroup for $id {
221
63
            type Id = $type_id;
222
504
            fn type_id(&self) -> Self::Id {
223
63
                #![allow(unused_imports)]
224
63
                use crate::relaycell::extlist::Ext as _;
225
504
                match self {
226
63
                    $(
227
63
                        $( #[$fmeta] )?
228
189
                        Self::$case(val) => val.type_id(),
229
63
                    )*
230
315
                    Self::Unrecognized(unrecognized) => unrecognized.type_id,
231
63
                }
232
504
            }
233
63
        }
234
63
        $(
235
63
        $( #[$fmeta] )?
236
63
        impl From<$case> for $id {
237
5481
            fn from(val: $case) -> $id {
238
5481
                $id :: $case ( val )
239
5481
            }
240
63
        }
241
63
        )*
242
63
        impl From<crate::relaycell::extlist::UnrecognizedExt<$type_id>> for $id {
243
63
            fn from(val: crate::relaycell::extlist::UnrecognizedExt<$type_id>) -> $id {
244
63
                $id :: Unrecognized(val)
245
63
            }
246
        }
247

            
248
        impl ExtList<$id> {
249
            $(
250
                /// Return the first
251
                #[doc = stringify!($case)]
252
                // extension in this ExtList, or None if no such extension exists.
253
                #[allow(dead_code)]
254
                $( #[$fmeta] )?
255
                pub(super) fn [<get_ $case:snake:lower>](&self) -> Option<&$case> {
256
                    self.extensions.iter().find_map(|ext| {
257
                        match ext {
258
                            $id :: $case(x) => Some(x),
259
                            _ => None,
260
                        }
261
                    })
262
                }
263
            )*
264
        }
265
}}
266
}
267
pub(super) use decl_extension_group;