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;
124    use alloc::vec::Vec;
125    use byteorder::{BigEndian, ByteOrder};
126    use tracing::trace;
127
128    #[test]
129    fn nonce() {
130        let _log = crate::tests::test_init_log();
131        let attr = Nonce::new("nonce").unwrap();
132        trace!("{attr}");
133        assert_eq!(attr.nonce(), "nonce");
134        assert_eq!(attr.length() as usize, "nonce".len());
135    }
136
137    #[test]
138    fn nonce_raw() {
139        let _log = crate::tests::test_init_log();
140        let attr = Nonce::new("nonce").unwrap();
141        let raw = RawAttribute::from(&attr);
142        trace!("{raw}");
143        assert_eq!(raw.get_type(), Nonce::TYPE);
144        let mapped2 = Nonce::try_from(&raw).unwrap();
145        assert_eq!(mapped2.nonce(), "nonce");
146    }
147
148    #[test]
149    fn nonce_raw_wrong_type() {
150        let _log = crate::tests::test_init_log();
151        let attr = Nonce::new("nonce").unwrap();
152        let raw = RawAttribute::from(&attr);
153
154        // provide incorrectly typed data
155        let mut data: Vec<_> = raw.into();
156        BigEndian::write_u16(&mut data[0..2], 0);
157        assert!(matches!(
158            Nonce::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
159            Err(StunParseError::WrongAttributeImplementation)
160        ));
161    }
162
163    #[test]
164    fn nonce_write_into() {
165        let _log = crate::tests::test_init_log();
166        let attr = Nonce::new("nonce").unwrap();
167        let raw = RawAttribute::from(&attr);
168
169        let mut dest = vec![0; raw.padded_len()];
170        attr.write_into(&mut dest).unwrap();
171        let raw = RawAttribute::from_bytes(&dest).unwrap();
172        let nonce = Nonce::try_from(&raw).unwrap();
173        assert_eq!(nonce.nonce(), "nonce");
174    }
175
176    #[test]
177    #[should_panic(expected = "out of range")]
178    fn nonce_write_into_unchecked() {
179        let _log = crate::tests::test_init_log();
180        let attr = Nonce::new("nonce").unwrap();
181        let raw = RawAttribute::from(&attr);
182
183        let mut dest = vec![0; raw.padded_len() - 1];
184        attr.write_into_unchecked(&mut dest);
185    }
186
187    #[test]
188    fn nonce_not_utf8() {
189        let _log = crate::tests::test_init_log();
190        let attr = Nonce::new("nonce").unwrap();
191        let raw = RawAttribute::from(&attr);
192        let mut data = raw.to_bytes();
193        data[6] = 0x88;
194        assert!(matches!(
195            Nonce::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
196            Err(StunParseError::InvalidAttributeData)
197        ));
198    }
199
200    #[test]
201    fn nonce_new_too_large() {
202        let _log = crate::tests::test_init_log();
203        let mut large = String::new();
204        for _i in 0..95 {
205            large.push_str("abcdefgh");
206        }
207        large.push_str("abcd");
208        assert!(matches!(
209            Nonce::new(&large),
210            Err(StunWriteError::TooLarge {
211                expected: 763,
212                actual: 764
213            })
214        ));
215    }
216}