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