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