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