stun_rs/attributes/stun/
realm.rs

1use crate::attributes::{stunt_attribute, DecodeAttributeValue, EncodeAttributeValue};
2use crate::context::{AttributeDecoderContext, AttributeEncoderContext};
3use crate::error::{StunError, StunErrorType};
4use crate::strings::QuotedString;
5use crate::{strings, Decode, Encode};
6use precis_core::profile::PrecisFastInvocation;
7use precis_profiles::OpaqueString;
8use std::convert::TryFrom;
9
10const REALM: u16 = 0x0014;
11
12const MAX_ENCODED_SIZE: usize = 509;
13const MAX_DECODED_SIZE: usize = 763;
14
15/// The REALM attribute may be present in requests and responses. It
16/// contains text that meets the grammar for "realm-value" as described
17/// in [`RFC3261`](https://datatracker.ietf.org/doc/html/rfc3261)
18/// but without the double quotes and their surrounding
19/// white space.  That is, it is an unquoted realm-value (and is therefore
20/// a sequence of `qdtext` or quoted-pair).
21///
22/// # Examples
23///```rust
24/// # use std::convert::TryFrom;
25/// # use stun_rs::attributes::stun::Realm;
26/// # use stun_rs::StunErrorType;
27/// # use std::error::Error;
28/// #
29/// # fn main() -> Result<(), Box<dyn Error>> {
30/// // Create a realm attribute with an input string that meets
31/// // the grammar for `realm-value`
32/// let attr = Realm::try_from("example.org")?;
33/// // Realm uses the OpaqueString profile to compare strings
34/// assert_eq!(attr, "example.org");
35///
36/// // Next input string does not meet the grammar for `realm-value`
37/// let result = Realm::try_from("\u{fd}\u{80}");
38/// assert_eq!(result.expect_err("Error expected"), StunErrorType::InvalidParam);
39/// #
40/// #  Ok(())
41/// # }
42///```
43#[derive(Debug, PartialEq, Clone, Hash, Eq, PartialOrd, Ord)]
44pub struct Realm(QuotedString);
45
46impl Realm {
47    /// Creates a [`Realm`] if the value provided meets the grammar for `realm-value`
48    /// and can be processed using the `OpaqueString` profile
49    /// [`RFC8265`](https://datatracker.ietf.org/doc/html/rfc8265)
50    pub fn new<S>(value: S) -> Result<Self, StunError>
51    where
52        S: AsRef<str>,
53    {
54        let realm = strings::opaque_string_prepapre(value.as_ref())?;
55        let realm = QuotedString::try_from(realm.as_ref())?;
56        let realm_len = realm.as_str().len();
57        (realm_len <= MAX_ENCODED_SIZE)
58            .then_some(Realm(realm))
59            .ok_or_else(|| {
60                StunError::new(
61                    StunErrorType::ValueTooLong,
62                    format!(
63                        "Value length {} > max. encoded size {}",
64                        realm_len, MAX_ENCODED_SIZE
65                    ),
66                )
67            })
68    }
69
70    /// Returns a slice representation of the realm value
71    pub fn as_str(&self) -> &str {
72        self.0.as_str()
73    }
74}
75
76impl PartialEq<&str> for Realm {
77    fn eq(&self, other: &&str) -> bool {
78        OpaqueString::compare(self, *other).unwrap_or(false)
79    }
80}
81
82impl PartialEq<Realm> for &str {
83    fn eq(&self, other: &Realm) -> bool {
84        OpaqueString::compare(*self, other).unwrap_or(false)
85    }
86}
87
88impl PartialEq<str> for Realm {
89    fn eq(&self, other: &str) -> bool {
90        OpaqueString::compare(self, other).unwrap_or(false)
91    }
92}
93
94impl PartialEq<String> for Realm {
95    fn eq(&self, other: &String) -> bool {
96        OpaqueString::compare(self, other).unwrap_or(false)
97    }
98}
99
100impl PartialEq<Realm> for String {
101    fn eq(&self, other: &Realm) -> bool {
102        OpaqueString::compare(self.as_str(), other).unwrap_or(false)
103    }
104}
105
106impl AsRef<str> for Realm {
107    fn as_ref(&self) -> &str {
108        self.0.as_ref()
109    }
110}
111
112impl AsRef<String> for Realm {
113    fn as_ref(&self) -> &String {
114        self.0.as_ref()
115    }
116}
117
118impl TryFrom<&str> for Realm {
119    type Error = StunError;
120
121    fn try_from(value: &str) -> Result<Self, Self::Error> {
122        Realm::new(value)
123    }
124}
125
126impl TryFrom<&String> for Realm {
127    type Error = StunError;
128
129    fn try_from(value: &String) -> Result<Self, Self::Error> {
130        Realm::new(value)
131    }
132}
133
134impl TryFrom<String> for Realm {
135    type Error = StunError;
136
137    fn try_from(value: String) -> Result<Self, Self::Error> {
138        Realm::new(value)
139    }
140}
141
142impl DecodeAttributeValue for Realm {
143    fn decode(ctx: AttributeDecoderContext) -> Result<(Self, usize), StunError> {
144        let raw_value = ctx.raw_value();
145
146        if raw_value.len() > MAX_DECODED_SIZE {
147            return Err(StunError::new(
148                StunErrorType::ValueTooLong,
149                format!(
150                    "Value length {} > max. decoded size {}",
151                    raw_value.len(),
152                    MAX_DECODED_SIZE
153                ),
154            ));
155        }
156
157        let (quoted, size) = QuotedString::decode(raw_value)?;
158
159        Ok((Self(quoted), size))
160    }
161}
162
163impl EncodeAttributeValue for Realm {
164    fn encode(&self, mut ctx: AttributeEncoderContext) -> Result<usize, StunError> {
165        if self.as_str().len() > MAX_ENCODED_SIZE {
166            return Err(StunError::new(
167                StunErrorType::ValueTooLong,
168                format!(
169                    "Value length {} > max. encoded size {}",
170                    self.as_str().len(),
171                    MAX_ENCODED_SIZE
172                ),
173            ));
174        }
175
176        self.0.encode(ctx.raw_value_mut())
177    }
178}
179
180impl crate::attributes::AsVerifiable for Realm {}
181
182stunt_attribute!(Realm, REALM);
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187    use crate::error::StunErrorType;
188    use crate::StunAttribute;
189
190    #[test]
191    fn constructor_realm() {
192        let value = String::from("realm");
193        let realm_1 = Realm::new(&value).expect("Can not create REALM attribute");
194        let realm_2 = Realm::new(&value).expect("Can not create REALM attribute");
195        assert_eq!(realm_1, "realm");
196        assert_eq!("realm", realm_1);
197        assert_eq!(value, realm_1);
198        assert_eq!(realm_1, value);
199        assert_eq!(realm_1, realm_2);
200
201        let val: &String = realm_1.as_ref();
202        assert!(value.eq(val));
203
204        let value: &str = realm_1.as_ref();
205        assert!(value.eq(val));
206
207        // Opaque string does not allow empty labels
208        let result = Realm::try_from("");
209        assert_eq!(
210            result.expect_err("Error expected"),
211            StunErrorType::InvalidParam
212        );
213
214        // Control characters like TAB `U+0009` are disallowed
215        let result = Realm::try_from("bad\u{0009}realm");
216        assert_eq!(
217            result.expect_err("Error expected"),
218            StunErrorType::InvalidParam
219        );
220
221        let value = "x".repeat(MAX_ENCODED_SIZE);
222        let _result = Realm::try_from(value).expect("Can not create a Realm attribute");
223
224        let value = "x".repeat(MAX_ENCODED_SIZE + 1);
225        let result = Realm::try_from(&value);
226        assert_eq!(
227            result.expect_err("Error expected"),
228            StunErrorType::ValueTooLong
229        );
230    }
231
232    #[test]
233    fn decode_realm_value() {
234        let dummy_msg = [];
235        // Realm: example.org
236        let buffer = [
237            0x65, 0x78, 0x61, 0x6d, // }
238            0x70, 0x6c, 0x65, 0x2e, // }  Realm value (11 bytes) and padding (1 byte)
239            0x6f, 0x72, 0x67, // }
240        ];
241        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
242
243        let (realm, size) = Realm::decode(ctx).expect("Can not decode REALM");
244        assert_eq!(size, 11);
245        assert_eq!(realm.as_str(), "example.org");
246
247        let value = "x".repeat(MAX_DECODED_SIZE);
248        let ctx = AttributeDecoderContext::new(None, &dummy_msg, value.as_bytes());
249        let (_realm, size) = Realm::decode(ctx).expect("Can not decode NONCE");
250        assert_eq!(size, MAX_DECODED_SIZE);
251    }
252
253    #[test]
254    fn decode_realm_value_error() {
255        let dummy_msg = [];
256        // Realm: example.org\0
257        let buffer = [
258            0x65, 0x78, 0x61, 0x6d, // }
259            0x70, 0x6c, 0x65, 0x2e, // }  Realm value (11 bytes) and padding (1 byte)
260            0x6f, 0x72, 0x67, 0x00, // }
261        ];
262        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
263
264        // Character 0x00 is disallowed by `OpaqueString`
265        assert_eq!(
266            Realm::decode(ctx).expect_err("Error expected"),
267            StunErrorType::InvalidParam
268        );
269
270        // Realm: "example.org"
271        let buffer = [
272            0x22, // } Double quote
273            0x65, 0x78, 0x61, 0x6d, // }
274            0x70, 0x6c, 0x65, 0x2e, // }  Realm value (11 bytes) and padding (1 byte)
275            0x6f, 0x72, 0x67, 0x00, // }
276            0x22, // } Double quote
277        ];
278        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
279
280        // Not double quoted allowed
281        assert_eq!(
282            Realm::decode(ctx).expect_err("Error expected"),
283            StunErrorType::InvalidParam
284        );
285
286        let value = "x".repeat(MAX_DECODED_SIZE + 1);
287        let ctx = AttributeDecoderContext::new(None, &dummy_msg, value.as_bytes());
288        assert_eq!(
289            Realm::decode(ctx).expect_err("Error expected"),
290            StunErrorType::ValueTooLong
291        );
292    }
293
294    #[test]
295    fn encode_realm_value() {
296        let dummy_msg: [u8; 0] = [0x0; 0];
297        let realm = Realm::try_from("   example.org   ").expect("Expected QuotedString");
298
299        let mut buffer: [u8; 11] = [0x0; 11];
300        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
301        let result = realm.encode(ctx);
302        assert_eq!(result, Ok(11));
303
304        let mut buffer: [u8; MAX_ENCODED_SIZE] = [0x0; MAX_ENCODED_SIZE];
305        let realm = Realm::try_from("x".repeat(MAX_ENCODED_SIZE))
306            .expect("Can not create a Realm attribute");
307        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
308        let result = realm.encode(ctx);
309        assert_eq!(result, Ok(MAX_ENCODED_SIZE));
310    }
311
312    #[test]
313    fn encode_realm_value_error() {
314        let dummy_msg: [u8; 0] = [0x0; 0];
315        let realm = Realm::try_from("   example.org   ").expect("Expected QuotedString");
316
317        let mut buffer: [u8; 10] = [0x0; 10];
318        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
319        let result = realm.encode(ctx);
320        assert_eq!(
321            result.expect_err("Error expected"),
322            StunErrorType::SmallBuffer
323        );
324
325        let mut buffer: [u8; MAX_ENCODED_SIZE + 1] = [0x0; MAX_ENCODED_SIZE + 1];
326        let str = "x".repeat(MAX_ENCODED_SIZE + 1);
327        let value = QuotedString::try_from(str.as_str()).expect("Expected QuotedString");
328        let realm = Realm(value);
329        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
330        let result = realm.encode(ctx);
331        assert_eq!(
332            result.expect_err("Error expected"),
333            StunErrorType::ValueTooLong
334        );
335    }
336
337    #[test]
338    fn realm_stunt_attribute() {
339        let attr = StunAttribute::Realm(Realm::try_from("test").expect("Expected QuotedString"));
340        assert!(attr.is_realm());
341        assert!(attr.as_realm().is_ok());
342        assert!(attr.as_unknown().is_err());
343
344        assert!(attr.attribute_type().is_comprehension_required());
345        assert!(!attr.attribute_type().is_comprehension_optional());
346
347        let dbg_fmt = format!("{:?}", attr);
348        assert_eq!("Realm(Realm(QuotedString(\"test\")))", dbg_fmt);
349    }
350}