stun_rs/
attributes.rs

1//! STUN Attributes.
2//! This module contains all attributes defined for the STUN protocol.
3//! Additional flags can be enabled for TURN and ICE.
4
5use crate::context::{AttributeDecoderContext, AttributeEncoderContext};
6use crate::error::StunError;
7use crate::DecoderContext;
8use std::fmt;
9
10mod address_port;
11mod integrity_attr;
12
13mod unknown;
14pub use unknown::Unknown;
15
16pub mod stun;
17
18#[cfg(feature = "ice")]
19pub mod ice;
20
21#[cfg(feature = "turn")]
22pub mod turn;
23
24#[cfg(feature = "mobility")]
25pub mod mobility;
26
27#[cfg(feature = "discovery")]
28pub mod discovery;
29
30/// Trait implemented by all [`StunAttribute`] that required validation
31/// when they are decoded
32pub(crate) trait Verifiable {
33    /// Performs attribute validation on decoding
34    /// # Arguments:
35    /// - `input`: raw bytes buffer
36    /// - `ctx`: the decoder context
37    /// # Returns
38    /// `True` is the validations success or `False` if there is an error during the validation
39    fn verify(&self, input: &[u8], cxt: &DecoderContext) -> bool;
40}
41
42pub(crate) trait AsVerifiable {
43    fn as_verifiable_ref(&self) -> Option<&dyn Verifiable> {
44        None
45    }
46}
47
48pub(crate) trait EncodeAttributeValue {
49    fn encode(&self, ctx: AttributeEncoderContext) -> Result<usize, StunError>;
50    fn post_encode(&self, _ctx: AttributeEncoderContext) -> Result<(), StunError> {
51        Ok(())
52    }
53}
54
55pub(crate) trait DecodeAttributeValue {
56    fn decode(ctx: AttributeDecoderContext) -> Result<(Self, usize), StunError>
57    where
58        Self: Sized;
59}
60
61/// A STUN attribute type is a hex number in the range 0x0000-0xFFFF.
62/// STUN attribute types in the range 0x0000-0x7FFF are considered
63/// comprehension-required.
64///
65/// # Examples
66///```rust
67/// # use stun_rs::AttributeType;
68/// let attr_type = AttributeType::from(0x0008);
69/// assert_eq!(attr_type.as_u16(), 0x0008);
70/// // This is a comprehension required attribute
71/// assert!(attr_type.is_comprehension_required());
72/// // This is not a comprehension optional attribute
73/// assert!(!attr_type.is_comprehension_optional());
74///```
75#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
76pub struct AttributeType(u16);
77impl AttributeType {
78    /// Creates a new [`AttributeType` ]
79    pub fn new(attr_type: u16) -> Self {
80        AttributeType(attr_type)
81    }
82
83    /// Return the [`u16`] representation of this attribute type
84    pub fn as_u16(&self) -> u16 {
85        self.0
86    }
87
88    /// Returns true if this is a comprehension required attribute
89    pub fn is_comprehension_required(&self) -> bool {
90        // Comprehension-required range (0x0000-0x7FFF):
91        self.0 < 0x8000
92    }
93
94    /// Returns true if this is a comprehension optional attribute
95    pub fn is_comprehension_optional(self) -> bool {
96        // Comprehension-optional range (0x8000-0xFFFF)
97        !self.is_comprehension_required()
98    }
99}
100
101impl From<u16> for AttributeType {
102    fn from(val: u16) -> Self {
103        Self::new(val)
104    }
105}
106
107impl From<AttributeType> for u16 {
108    fn from(val: AttributeType) -> Self {
109        val.0
110    }
111}
112
113impl fmt::Debug for AttributeType {
114    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115        write!(f, "AttributeType (0x{:04X})", self.0)?;
116        Ok(())
117    }
118}
119
120impl fmt::Display for AttributeType {
121    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
122        write!(f, "attribute type (0x{:04X})", self.0)?;
123        Ok(())
124    }
125}
126
127/// Trait implemented by all [`StunAttribute`]
128pub trait StunAttributeType {
129    /// Returns the STUN attribute type of this instance.
130    fn attribute_type(&self) -> AttributeType;
131
132    /// Returns the STUN attribute type.
133    fn get_type() -> AttributeType
134    where
135        Self: Sized;
136}
137
138macro_rules! stunt_attribute (
139    ($attr_class:ident, $attr_type:ident) => {
140        impl crate::attributes::StunAttributeType for $attr_class {
141            fn get_type() -> crate::attributes::AttributeType where Self: Sized {
142                crate::attributes::AttributeType::from($attr_type)
143            }
144            fn attribute_type(&self) -> crate::attributes::AttributeType {
145                $attr_class::get_type()
146            }
147        }
148        impl From<$attr_class> for crate::attributes::StunAttribute {
149            fn from(value: $attr_class) -> Self {
150                crate::attributes::StunAttribute::$attr_class(value)
151            }
152        }
153    }
154);
155pub(crate) use stunt_attribute;
156
157macro_rules! stunt_attribute_impl (
158    ($(($class:ident, $mod:ident $(, $flag:literal)?)),*) => {
159        paste::paste! {
160            /// STUN Attributes that can be attached to a [`StunMessage`](crate::StunMessage)
161            #[derive(Debug, Clone)]
162            pub enum StunAttribute {
163                $(
164                    $(#[cfg(feature = $flag)])?
165                    #[doc = "The `" $class "`atribute"]
166                    $class($mod::$class),
167                )*
168            }
169        }
170
171        impl AsVerifiable for StunAttribute {
172            fn as_verifiable_ref(&self) -> Option<&dyn Verifiable> {
173                match self {
174                    $(
175                        $(#[cfg(feature = $flag)])?
176                        StunAttribute::$class(attr) => attr.as_verifiable_ref(),
177                    )*
178                }
179            }
180        }
181
182        impl EncodeAttributeValue for StunAttribute {
183            fn encode(&self, ctx: AttributeEncoderContext) -> Result<usize, StunError> {
184                match self {
185                    $(
186                        $(#[cfg(feature = $flag)])?
187                        StunAttribute::$class(attr) => attr.encode(ctx),
188                    )*
189                }
190            }
191
192            fn post_encode(&self, ctx: AttributeEncoderContext) -> Result<(), StunError> {
193                match self {
194                    $(
195                        $(#[cfg(feature = $flag)])?
196                        StunAttribute::$class(attr) => attr.post_encode(ctx),
197                    )*
198                }
199            }
200        }
201
202        impl StunAttribute {
203            /// Returns the STUN attribute type of this instance.
204            pub fn attribute_type(&self) -> AttributeType {
205                match self {
206                    $(
207                        $(#[cfg(feature = $flag)])?
208                        StunAttribute::$class(attr) => attr.attribute_type(),
209                    )*
210                }
211            }
212
213            $(
214                paste::paste! {
215                    $(#[cfg(feature = $flag)])?
216                    #[doc = "Returns true if this `StunAttribute` is `" $class "`"]
217                    pub fn [<is_ $class:snake>] (&self) -> bool {
218                        matches!(self, StunAttribute::$class(_))
219                    }
220
221                    $(#[cfg(feature = $flag)])?
222                    #[doc = "Returns a reference to the internal attribute value or an error if the type of the attribute is not `" $class "`"]
223                    pub fn [<as_ $class:snake>] (&self) -> Result<&$mod::$class, crate::StunError> {
224                        match self {
225                            StunAttribute::$class(attr) => Ok(attr),
226                            _ => Err(crate::error::StunError::new(
227                                crate::error::StunErrorType::InvalidParam,
228                                format!("Attribute is not of type {}", std::stringify!($class))
229                            )),
230                        }
231                    }
232
233                    $(#[cfg(feature = $flag)])?
234                    #[doc = "Returns a reference to the `" $class "` attribute."]
235                    #[doc = "# Panics"]
236                    #[doc = "Panics if the attribute is not an `" $class  "`"]
237                    pub fn [<expect_ $class:snake>](&self) -> &$mod::$class {
238                        self.[<as_ $class:snake>]().unwrap()
239                    }
240                }
241            )*
242        }
243    }
244);
245pub(crate) use stunt_attribute_impl;
246
247stunt_attribute_impl!(
248    (Unknown, unknown),
249    // STUN Attributes
250    (AlternateServer, stun),
251    (ErrorCode, stun),
252    (Fingerprint, stun),
253    (MappedAddress, stun),
254    (MessageIntegrity, stun),
255    (MessageIntegritySha256, stun),
256    (Nonce, stun),
257    (PasswordAlgorithm, stun),
258    (PasswordAlgorithms, stun),
259    (Realm, stun),
260    (Software, stun),
261    (UnknownAttributes, stun),
262    (UserHash, stun),
263    (UserName, stun),
264    (XorMappedAddress, stun),
265    // ICE Attributes
266    (IceControlled, ice, "ice"),
267    (IceControlling, ice, "ice"),
268    (Priority, ice, "ice"),
269    (UseCandidate, ice, "ice"),
270    // TURN Attributes
271    (ChannelNumber, turn, "turn"),
272    (LifeTime, turn, "turn"),
273    (XorPeerAddress, turn, "turn"),
274    (XorRelayedAddress, turn, "turn"),
275    (Data, turn, "turn"),
276    (RequestedAddressFamily, turn, "turn"),
277    (EvenPort, turn, "turn"),
278    (DontFragment, turn, "turn"),
279    (RequestedTrasport, turn, "turn"),
280    (AdditionalAddressFamily, turn, "turn"),
281    (ReservationToken, turn, "turn"),
282    (AddressErrorCode, turn, "turn"),
283    (Icmp, turn, "turn"),
284    // Mobility
285    (MobilityTicket, mobility, "mobility"),
286    // Discovery
287    (ChangeRequest, discovery, "discovery"),
288    (OtherAddress, discovery, "discovery"),
289    (Padding, discovery, "discovery"),
290    (ResponseOrigin, discovery, "discovery"),
291    (ResponsePort, discovery, "discovery")
292);
293
294#[cfg(test)]
295mod tests {
296    use super::AttributeType;
297    use crate::common::check_buffer_boundaries;
298    use crate::error::StunErrorType;
299
300    #[test]
301    fn buffer_boundaries() {
302        let buffer = [];
303        assert!(check_buffer_boundaries(&buffer, 0).is_ok());
304        assert!(check_buffer_boundaries(&buffer, 1).is_err());
305        assert_eq!(
306            check_buffer_boundaries(&buffer, 1).expect_err("Error expected"),
307            StunErrorType::SmallBuffer
308        );
309
310        let buffer: [u8; 1] = [0; 1];
311        assert!(check_buffer_boundaries(&buffer, 0).is_ok());
312        assert!(check_buffer_boundaries(&buffer, 1).is_ok());
313        assert!(check_buffer_boundaries(&buffer, 2).is_err());
314        assert_eq!(
315            check_buffer_boundaries(&buffer, 2).expect_err("Error expected"),
316            StunErrorType::SmallBuffer
317        );
318    }
319
320    #[test]
321    fn fmt_attribute_type() {
322        let attr_str = format!("{:?}", AttributeType::from(0x1234));
323        assert_eq!("AttributeType (0x1234)", attr_str);
324
325        let attr_str = format!("{}", AttributeType::from(0x1234));
326        assert_eq!("attribute type (0x1234)", attr_str);
327    }
328}