Skip to main content

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