stun_rs/attributes/stun/
nonce.rs

1use crate::attributes::{stunt_attribute, DecodeAttributeValue, EncodeAttributeValue};
2use crate::context::{AttributeDecoderContext, AttributeEncoderContext};
3use crate::error::{StunError, StunErrorType};
4use crate::strings::QuotedString;
5use crate::{Decode, Encode};
6use std::convert::TryFrom;
7
8const NONCE: u16 = 0x0015;
9const MAX_ENCODED_SIZE: usize = 509;
10const MAX_DECODED_SIZE: usize = 763;
11
12/// The NONCE attribute may be present in requests and responses.  It
13/// contains a sequence of `qdtext` or `quoted-pair`, which are defined in
14/// [`RFC3261`](https://datatracker.ietf.org/doc/html/rfc3261).
15/// Note that this means that the NONCE attribute will not
16/// contain the actual surrounding quote characters.
17///
18/// # Examples
19///```rust
20/// # use std::convert::TryFrom;
21/// # use stun_rs::attributes::stun::Nonce;
22/// # use stun_rs::StunErrorType;
23/// # use std::error::Error;
24/// #
25/// # fn main() -> Result<(), Box<dyn Error>> {
26/// // Create a nonce attribute using an input string that is
27/// // sequence of `qdtext` or `quoted-pair`
28/// let attr = Nonce::try_from("f//499k954d6OL34oL9FSTvy64sA")?;
29/// assert_eq!(attr, "f//499k954d6OL34oL9FSTvy64sA");
30///
31/// // Next input string is not a valid sequence of `qdtext` or `quoted-pair`
32/// let result = Nonce::try_from("\u{fd}\u{80}");
33/// assert_eq!(result.expect_err("Error expected"), StunErrorType::InvalidParam);
34/// #
35/// #  Ok(())
36/// # }
37///```
38#[derive(Debug, PartialEq, Clone, Hash, Eq, PartialOrd, Ord)]
39pub struct Nonce(QuotedString);
40
41impl Nonce {
42    /// Creates a [`Nonce`] attribute if the value provided
43    /// is a valid sequence of `qdtext` or `quoted-pair`
44    pub fn new<S>(value: S) -> Result<Self, StunError>
45    where
46        S: AsRef<str>,
47    {
48        let name = QuotedString::try_from(value.as_ref())?;
49        let name_len = name.as_str().len();
50        (name_len <= MAX_ENCODED_SIZE)
51            .then_some(Nonce(name))
52            .ok_or_else(|| {
53                StunError::new(
54                    StunErrorType::ValueTooLong,
55                    format!(
56                        "Value length {} > max. encoded size {}",
57                        name_len, MAX_ENCODED_SIZE
58                    ),
59                )
60            })
61    }
62
63    /// Returns the slice representation of this None attribute
64    pub fn as_str(&self) -> &str {
65        self.0.as_str()
66    }
67}
68
69impl PartialEq<&str> for Nonce {
70    fn eq(&self, other: &&str) -> bool {
71        self.as_str().eq(*other)
72    }
73}
74
75impl PartialEq<Nonce> for &str {
76    fn eq(&self, other: &Nonce) -> bool {
77        other.as_str().eq(*self)
78    }
79}
80
81impl PartialEq<str> for Nonce {
82    fn eq(&self, other: &str) -> bool {
83        self.as_str().eq(other)
84    }
85}
86
87impl PartialEq<String> for Nonce {
88    fn eq(&self, other: &String) -> bool {
89        other.eq(self.as_str())
90    }
91}
92
93impl PartialEq<Nonce> for String {
94    fn eq(&self, other: &Nonce) -> bool {
95        self.eq(other.as_str())
96    }
97}
98
99impl AsRef<str> for Nonce {
100    fn as_ref(&self) -> &str {
101        self.0.as_ref()
102    }
103}
104
105impl AsRef<String> for Nonce {
106    fn as_ref(&self) -> &String {
107        self.0.as_ref()
108    }
109}
110
111impl TryFrom<&str> for Nonce {
112    type Error = StunError;
113
114    /// Returns a [`Nonce`] attribute if the value provided
115    /// is a valid sequence of `qdtext` or `quoted-pair`
116    fn try_from(value: &str) -> Result<Self, Self::Error> {
117        Nonce::new(value)
118    }
119}
120
121impl TryFrom<&String> for Nonce {
122    type Error = StunError;
123
124    /// Returns a [`Nonce`] attribute if the value provided
125    /// is a valid sequence of `qdtext` or `quoted-pair`
126    fn try_from(value: &String) -> Result<Self, Self::Error> {
127        Nonce::new(value)
128    }
129}
130
131impl TryFrom<String> for Nonce {
132    type Error = StunError;
133
134    /// Returns a [`Nonce`] attribute if the value provided
135    /// is a valid sequence of `qdtext` or `quoted-pair`
136    fn try_from(value: String) -> Result<Self, Self::Error> {
137        Nonce::new(value)
138    }
139}
140
141impl DecodeAttributeValue for Nonce {
142    fn decode(ctx: AttributeDecoderContext) -> Result<(Self, usize), StunError> {
143        let raw_value = ctx.raw_value();
144        if raw_value.len() > MAX_DECODED_SIZE {
145            return Err(StunError::new(
146                StunErrorType::ValueTooLong,
147                format!(
148                    "Value length {} is bigger than max. decoded size {}",
149                    raw_value.len(),
150                    MAX_DECODED_SIZE
151                ),
152            ));
153        }
154
155        let (quoted, size) = QuotedString::decode(raw_value)?;
156
157        Ok((Self(quoted), size))
158    }
159}
160
161impl EncodeAttributeValue for Nonce {
162    fn encode(&self, mut ctx: AttributeEncoderContext) -> Result<usize, StunError> {
163        if self.as_str().len() > MAX_ENCODED_SIZE {
164            return Err(StunError::new(
165                StunErrorType::ValueTooLong,
166                format!(
167                    "Value length {} is bigger than max. decoded size {}",
168                    self.as_str().len(),
169                    MAX_ENCODED_SIZE
170                ),
171            ));
172        }
173
174        self.0.encode(ctx.raw_value_mut())
175    }
176}
177
178impl crate::attributes::AsVerifiable for Nonce {}
179
180stunt_attribute!(Nonce, NONCE);
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185    use crate::StunAttribute;
186
187    #[test]
188    fn constructor() {
189        let value = String::from("f//499k954d6OL34oL9FSTvy64sA");
190        let attr_1 = Nonce::try_from(&value).expect("Can not create a Nonce attribute");
191        let attr_2 = Nonce::new(&value).expect("Can not create a Nonce attribute");
192        let attr_3 = Nonce::try_from(value.clone()).expect("Can not create Software attribute");
193
194        assert_eq!(attr_1, value);
195        assert_eq!(value, attr_1);
196        assert_eq!(attr_1, "f//499k954d6OL34oL9FSTvy64sA");
197        assert_eq!("f//499k954d6OL34oL9FSTvy64sA", attr_1);
198        assert_eq!(attr_1, attr_2);
199        assert_eq!(attr_1, attr_3);
200
201        let val: &String = attr_1.as_ref();
202        assert!(value.eq(val));
203
204        let value: &str = attr_1.as_ref();
205        assert!(value.eq(val));
206
207        let value = "x".repeat(MAX_ENCODED_SIZE);
208        let _result = Nonce::try_from(&value).expect("Can not create a Nonce attribute");
209
210        let value = "x".repeat(MAX_ENCODED_SIZE + 1);
211        let result = Nonce::try_from(&value);
212        assert_eq!(
213            result.expect_err("Error expected"),
214            StunErrorType::ValueTooLong
215        );
216    }
217
218    #[test]
219    fn decode_nonce_value() {
220        let dummy_msg = [];
221        // Nonce: f//499k954d6OL34oL9FSTvy64sA
222        let buffer = [
223            0x66, 0x2f, 0x2f, 0x34, // }
224            0x39, 0x39, 0x6b, 0x39, // }
225            0x35, 0x34, 0x64, 0x36, // }
226            0x4f, 0x4c, 0x33, 0x34, // } Nonce value
227            0x6f, 0x4c, 0x39, 0x46, // }
228            0x53, 0x54, 0x76, 0x79, // }
229            0x36, 0x34, 0x73, 0x41, //
230        ];
231        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
232
233        let (nonce, size) = Nonce::decode(ctx).expect("Can not decode NONCE");
234        assert_eq!(size, 28);
235        assert_eq!(nonce.as_str(), "f//499k954d6OL34oL9FSTvy64sA");
236
237        let value = "x".repeat(MAX_DECODED_SIZE);
238        let ctx = AttributeDecoderContext::new(None, &dummy_msg, value.as_bytes());
239        let (_nonce, size) = Nonce::decode(ctx).expect("Can not decode NONCE");
240        assert_eq!(size, MAX_DECODED_SIZE);
241    }
242
243    #[test]
244    fn decode_nonce_value_error() {
245        let dummy_msg = [];
246        // Nonce: "f//499k954d6OL34oL9FSTvy64sA"
247        let buffer = [
248            0x22, // } Double quote
249            0x66, 0x2f, 0x2f, 0x34, // }
250            0x39, 0x39, 0x6b, 0x39, // }
251            0x35, 0x34, 0x64, 0x36, // }
252            0x4f, 0x4c, 0x33, 0x34, // } Nonce value
253            0x6f, 0x4c, 0x39, 0x46, // }
254            0x53, 0x54, 0x76, 0x79, // }
255            0x36, 0x34, 0x73, 0x41, //
256            0x22, // } Double quote
257        ];
258        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
259
260        assert_eq!(
261            Nonce::decode(ctx).expect_err("Error expected"),
262            StunErrorType::InvalidParam
263        );
264
265        let value = "x".repeat(MAX_DECODED_SIZE + 1);
266        let ctx = AttributeDecoderContext::new(None, &dummy_msg, value.as_bytes());
267        assert_eq!(
268            Nonce::decode(ctx).expect_err("Error expected"),
269            StunErrorType::ValueTooLong
270        );
271    }
272
273    #[test]
274    fn encode_nonce_value() {
275        let dummy_msg: [u8; 0] = [0x0; 0];
276        let nonce = Nonce::try_from("f//499k954d6OL34oL9FSTvy64sA").expect("Expected QuotedString");
277
278        let mut buffer: [u8; 28] = [0x0; 28];
279        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
280
281        let result = nonce.encode(ctx);
282        assert_eq!(result, Ok(28));
283
284        let mut buffer: [u8; MAX_ENCODED_SIZE] = [0x0; MAX_ENCODED_SIZE];
285        let nonce = Nonce::try_from("x".repeat(MAX_ENCODED_SIZE))
286            .expect("Can not create a Nonce attribute");
287        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
288        let result = nonce.encode(ctx);
289        assert_eq!(result, Ok(MAX_ENCODED_SIZE));
290    }
291
292    #[test]
293    fn encode_nonce_value_error() {
294        let dummy_msg: [u8; 0] = [0x0; 0];
295        let nonce = Nonce::try_from("f//499k954d6OL34oL9FSTvy64sA").expect("Expected QuotedString");
296
297        let mut buffer: [u8; 27] = [0x0; 27];
298        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
299        let result = nonce.encode(ctx);
300        assert_eq!(
301            result.expect_err("Error expected"),
302            StunErrorType::SmallBuffer
303        );
304
305        let mut buffer: [u8; MAX_ENCODED_SIZE + 1] = [0x0; MAX_ENCODED_SIZE + 1];
306        let str = "x".repeat(MAX_ENCODED_SIZE + 1);
307        let value = QuotedString::try_from(str.as_str()).expect("Expected QuotedString");
308        let nonce = Nonce(value);
309        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
310        let result = nonce.encode(ctx);
311        assert_eq!(
312            result.expect_err("Error expected"),
313            StunErrorType::ValueTooLong
314        );
315    }
316
317    #[test]
318    fn nonce_stunt_attribute() {
319        let attr =
320            StunAttribute::Nonce(Nonce::try_from("test").expect("Can not create Nonce attribute"));
321        assert!(attr.is_nonce());
322        assert!(attr.as_nonce().is_ok());
323        assert!(attr.as_unknown().is_err());
324
325        assert!(attr.attribute_type().is_comprehension_required());
326        assert!(!attr.attribute_type().is_comprehension_optional());
327
328        let dbg_fmt = format!("{:?}", attr);
329        assert_eq!("Nonce(Nonce(QuotedString(\"test\")))", dbg_fmt);
330    }
331}