Skip to main content

stun_types/attribute/
realm.rs

1// Copyright (C) 2020 Matthew Waters <matthew@centricular.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8//
9// SPDX-License-Identifier: MIT OR Apache-2.0
10
11use alloc::borrow::ToOwned;
12use alloc::string::{String, ToString};
13use core::convert::TryFrom;
14
15use crate::message::{StunParseError, StunWriteError};
16
17use super::{
18    Attribute, AttributeExt, AttributeFromRaw, AttributeStaticType, AttributeType, AttributeWrite,
19    AttributeWriteExt, RawAttribute,
20};
21
22/// The Realm [`Attribute`]
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct Realm {
25    realm: String,
26}
27
28impl AttributeStaticType for Realm {
29    const TYPE: AttributeType = AttributeType(0x0014);
30}
31
32impl Attribute for Realm {
33    fn get_type(&self) -> AttributeType {
34        Self::TYPE
35    }
36
37    fn length(&self) -> u16 {
38        self.realm.len() as u16
39    }
40}
41
42impl AttributeWrite for Realm {
43    fn to_raw(&self) -> RawAttribute<'_> {
44        RawAttribute::new(Realm::TYPE, self.realm.as_bytes())
45    }
46    fn write_into_unchecked(&self, dest: &mut [u8]) {
47        let len = self.padded_len();
48        self.write_header_unchecked(dest);
49        let offset = 4 + self.realm.len();
50        dest[4..offset].copy_from_slice(self.realm.as_bytes());
51        if len > offset {
52            dest[offset..len].fill(0);
53        }
54    }
55}
56
57impl AttributeFromRaw<'_> for Realm {
58    fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
59    where
60        Self: Sized,
61    {
62        Self::try_from(raw)
63    }
64}
65
66impl TryFrom<&RawAttribute<'_>> for Realm {
67    type Error = StunParseError;
68
69    fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
70        raw.check_type_and_len(Self::TYPE, ..=763)?;
71        Ok(Self {
72            realm: core::str::from_utf8(&raw.value)
73                .map_err(|_| StunParseError::InvalidAttributeData)?
74                .to_owned(),
75        })
76    }
77}
78
79impl Realm {
80    /// Create a new Realm [`Attribute`]
81    ///
82    /// # Examples
83    ///
84    /// ```
85    /// # use stun_types::attribute::*;
86    /// let realm = Realm::new("realm").unwrap();
87    /// assert_eq!(realm.realm(), "realm");
88    /// ```
89    pub fn new(realm: &str) -> Result<Self, StunWriteError> {
90        if realm.len() > 763 {
91            return Err(StunWriteError::TooLarge {
92                expected: 763,
93                actual: realm.len(),
94            });
95        }
96        Ok(Self {
97            realm: realm.to_string(),
98        })
99    }
100
101    /// Retrieve the realm value
102    ///
103    /// # Examples
104    ///
105    /// ```
106    /// # use stun_types::attribute::*;
107    /// let realm = Realm::new("realm").unwrap();
108    /// assert_eq!(realm.realm(), "realm");
109    /// ```
110    pub fn realm(&self) -> &str {
111        &self.realm
112    }
113}
114
115impl core::fmt::Display for Realm {
116    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
117        write!(f, "{}: {}", Self::TYPE, self.realm)
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124    use alloc::vec;
125    use alloc::vec::Vec;
126    use byteorder::{BigEndian, ByteOrder};
127    use tracing::trace;
128
129    #[test]
130    fn realm() {
131        let _log = crate::tests::test_init_log();
132        let attr = Realm::new("realm").unwrap();
133        trace!("{attr}");
134        assert_eq!(attr.realm(), "realm");
135        assert_eq!(attr.length() as usize, "realm".len());
136    }
137
138    #[test]
139    fn realm_raw() {
140        let _log = crate::tests::test_init_log();
141        let attr = Realm::new("realm").unwrap();
142        let raw = RawAttribute::from(&attr);
143        trace!("{raw}");
144        assert_eq!(raw.get_type(), Realm::TYPE);
145        let mapped2 = Realm::try_from(&raw).unwrap();
146        assert_eq!(mapped2.realm(), "realm");
147    }
148
149    #[test]
150    fn realm_raw_wrong_type() {
151        let _log = crate::tests::test_init_log();
152        let attr = Realm::new("realm").unwrap();
153        let raw = RawAttribute::from(&attr);
154
155        // provide incorrectly typed data
156        let mut data: Vec<_> = raw.into();
157        BigEndian::write_u16(&mut data[0..2], 0);
158        assert!(matches!(
159            Realm::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
160            Err(StunParseError::WrongAttributeImplementation)
161        ));
162    }
163
164    #[test]
165    fn realm_write_into() {
166        let _log = crate::tests::test_init_log();
167        let attr = Realm::new("realm").unwrap();
168        let raw = RawAttribute::from(&attr);
169
170        let mut dest = vec![0; raw.padded_len()];
171        attr.write_into(&mut dest).unwrap();
172        let raw = RawAttribute::from_bytes(&dest).unwrap();
173        let realm = Realm::try_from(&raw).unwrap();
174        assert_eq!(realm.realm(), "realm");
175    }
176
177    #[test]
178    #[should_panic(expected = "out of range")]
179    fn realm_write_into_unchecked() {
180        let _log = crate::tests::test_init_log();
181        let attr = Realm::new("realm").unwrap();
182        let raw = RawAttribute::from(&attr);
183
184        let mut dest = vec![0; raw.padded_len() - 1];
185        attr.write_into_unchecked(&mut dest);
186    }
187
188    #[test]
189    fn realm_not_utf8() {
190        let _log = crate::tests::test_init_log();
191        let attr = Realm::new("realm").unwrap();
192        let raw = RawAttribute::from(&attr);
193        let mut data = raw.to_bytes();
194        data[6] = 0x88;
195        assert!(matches!(
196            Realm::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
197            Err(StunParseError::InvalidAttributeData)
198        ));
199    }
200
201    #[test]
202    fn realme_new_too_large() {
203        let _log = crate::tests::test_init_log();
204        let mut large = String::new();
205        for _i in 0..95 {
206            large.push_str("abcdefgh");
207        }
208        large.push_str("abcd");
209        assert!(matches!(
210            Realm::new(&large),
211            Err(StunWriteError::TooLarge {
212                expected: 763,
213                actual: 764
214            })
215        ));
216    }
217}