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