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