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