stun_rs/attributes/stun/
user_name.rs

1use crate::attributes::{stunt_attribute, DecodeAttributeValue, EncodeAttributeValue};
2use crate::context::{AttributeDecoderContext, AttributeEncoderContext};
3use crate::error::{StunError, StunErrorType};
4use crate::strings;
5use crate::{Decode, Encode};
6use precis_core::profile::PrecisFastInvocation;
7use precis_profiles::OpaqueString;
8use std::convert::TryFrom;
9
10const USER_NAME: u16 = 0x0006;
11
12const MAX_ENCODED_SIZE: usize = 509;
13const MAX_DECODED_SIZE: usize = 763;
14
15/// The USER-NAME attribute is used for message integrity.  It identifies
16/// the user name and password combination used in the message-integrity check.
17/// It MUST contain a UTF-8-encoded [`RFC3629`](https://datatracker.ietf.org/doc/html/rfc3629)
18/// sequence of fewer than 509 bytes and MUST have been processed using
19/// the `OpaqueString` profile [`RFC8265`](https://datatracker.ietf.org/doc/html/rfc8265)
20///
21/// # Examples
22///```rust
23/// # use std::convert::TryFrom;
24/// # use stun_rs::attributes::stun::UserName;
25/// # use stun_rs::StunErrorType;
26/// # use std::error::Error;
27/// #
28/// # fn main() -> Result<(), Box<dyn Error>> {
29/// let user_name = UserName::try_from("username")?;
30/// // UserName uses the OpaqueString profie to compare strings
31/// assert_eq!(user_name, "username");
32///
33/// // Control characters like TAB `U+0009` are disallowed by OpaqueString profile
34/// let result = UserName::try_from("user\u{0009}name");
35/// assert_eq!(result.expect_err("Error expected"), StunErrorType::InvalidParam);
36/// #
37/// #  Ok(())
38/// # }
39///```
40#[derive(Debug, PartialEq, Eq, Clone)]
41pub struct UserName(String);
42
43impl UserName {
44    /// Creates a [`UserName`] if the value provided can be processed using the
45    /// `OpaqueString` profile [`RFC8265`](https://datatracker.ietf.org/doc/html/rfc8265)
46    pub fn new<S>(value: S) -> Result<Self, StunError>
47    where
48        S: AsRef<str>,
49    {
50        let name = strings::opaque_string_prepapre(value.as_ref())?;
51        (name.len() < MAX_ENCODED_SIZE)
52            .then(|| UserName(String::from(name.as_ref())))
53            .ok_or_else(|| {
54                StunError::new(
55                    StunErrorType::ValueTooLong,
56                    format!(
57                        "Name length {} > max. allowed size {}",
58                        name.len(),
59                        MAX_ENCODED_SIZE
60                    ),
61                )
62            })
63    }
64
65    /// Returns a slice representation of the user name value
66    pub fn as_str(&self) -> &str {
67        self.0.as_str()
68    }
69}
70
71impl PartialEq<&str> for UserName {
72    fn eq(&self, other: &&str) -> bool {
73        OpaqueString::compare(self, *other).unwrap_or(false)
74    }
75}
76
77impl PartialEq<UserName> for &str {
78    fn eq(&self, other: &UserName) -> bool {
79        OpaqueString::compare(*self, other).unwrap_or(false)
80    }
81}
82
83impl PartialEq<str> for UserName {
84    fn eq(&self, other: &str) -> bool {
85        OpaqueString::compare(self, other).unwrap_or(false)
86    }
87}
88
89impl PartialEq<String> for UserName {
90    fn eq(&self, other: &String) -> bool {
91        OpaqueString::compare(self, other).unwrap_or(false)
92    }
93}
94
95impl PartialEq<UserName> for String {
96    fn eq(&self, other: &UserName) -> bool {
97        OpaqueString::compare(self, other).unwrap_or(false)
98    }
99}
100
101impl AsRef<str> for UserName {
102    fn as_ref(&self) -> &str {
103        self.as_str()
104    }
105}
106
107impl AsRef<String> for UserName {
108    fn as_ref(&self) -> &String {
109        &self.0
110    }
111}
112
113impl TryFrom<&str> for UserName {
114    type Error = StunError;
115
116    fn try_from(value: &str) -> Result<Self, Self::Error> {
117        UserName::new(value)
118    }
119}
120
121impl TryFrom<&String> for UserName {
122    type Error = StunError;
123
124    fn try_from(value: &String) -> Result<Self, Self::Error> {
125        UserName::new(value)
126    }
127}
128
129impl TryFrom<String> for UserName {
130    type Error = StunError;
131
132    fn try_from(value: String) -> Result<Self, Self::Error> {
133        UserName::new(value)
134    }
135}
136
137impl DecodeAttributeValue for UserName {
138    fn decode(ctx: AttributeDecoderContext) -> Result<(Self, usize), StunError> {
139        let (str, size) = <&'_ str as Decode<'_>>::decode(ctx.raw_value())?;
140
141        if size > MAX_DECODED_SIZE {
142            return Err(StunError::new(
143                StunErrorType::ValueTooLong,
144                format!(
145                    "Value length {} > max. decoded size {}",
146                    size, MAX_DECODED_SIZE
147                ),
148            ));
149        }
150
151        let name = strings::opaque_string_enforce(str)?;
152        Ok((UserName(String::from(name.as_ref())), size))
153    }
154}
155
156impl EncodeAttributeValue for UserName {
157    fn encode(&self, mut ctx: AttributeEncoderContext) -> Result<usize, StunError> {
158        if self.as_str().len() >= MAX_ENCODED_SIZE {
159            return Err(StunError::new(
160                StunErrorType::ValueTooLong,
161                format!(
162                    "Value length {} >= max. encoded size {}",
163                    self.as_str().len(),
164                    MAX_ENCODED_SIZE
165                ),
166            ));
167        }
168        self.0.as_str().encode(ctx.raw_value_mut())
169    }
170}
171
172impl crate::attributes::AsVerifiable for UserName {}
173
174stunt_attribute!(UserName, USER_NAME);
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179    use crate::error::StunErrorType;
180    use crate::StunAttribute;
181
182    #[test]
183    fn user_name_constructor() {
184        let name = String::from("username");
185        let user_name = UserName::try_from(&name).expect("Can not create USERNAME attribute");
186        // Next comparison will use the OpaqueString profile to compare strings
187        assert_eq!(user_name, "username");
188        assert_eq!("username", user_name);
189        assert_eq!(name, user_name);
190        assert_eq!(user_name, name);
191
192        let name = "x".repeat(MAX_ENCODED_SIZE - 1);
193        let user_name = UserName::try_from(&name).expect("Can not create USERNAME attribute");
194
195        let val: &String = user_name.as_ref();
196        assert!(name.eq(val));
197
198        let val: &str = user_name.as_ref();
199        assert!(name.eq(val));
200
201        let name = "x".repeat(MAX_ENCODED_SIZE);
202        // Username must be an UTF-8-encoded sequence of fewer than 509 bytes.
203        let result = UserName::try_from(name);
204        assert_eq!(
205            result.expect_err("Error expected"),
206            StunErrorType::ValueTooLong
207        );
208
209        // Opaque string does not allow empty labels
210        let result = UserName::try_from("");
211        assert_eq!(
212            result.expect_err("Error expected"),
213            StunErrorType::InvalidParam
214        );
215
216        // Control characters like TAB `U+0009` are disallowed
217        let result = UserName::try_from("user\u{0009}name");
218        assert_eq!(
219            result.expect_err("Error expected"),
220            StunErrorType::InvalidParam
221        );
222    }
223
224    #[test]
225    fn decode_user_name() {
226        let dummy_msg = [];
227        // `Username`: `username`
228        let buffer = [0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65];
229        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
230
231        let (user_name, size) = UserName::decode(ctx).expect("Can not decode USERNAME");
232        assert_eq!(size, 8);
233        assert_eq!(user_name.as_str(), "username");
234
235        let value = "x".repeat(MAX_DECODED_SIZE);
236        let ctx = AttributeDecoderContext::new(None, &dummy_msg, value.as_bytes());
237        let (_username, size) = UserName::decode(ctx).expect("Can not decode USERNAME");
238        assert_eq!(size, MAX_DECODED_SIZE);
239    }
240
241    #[test]
242    fn decode_error() {
243        let dummy_msg = [];
244
245        // Control characters are not allowed in opaque string profile
246        // Next buffer contains the control TAB character U+0009
247        let buffer = [0x75, 0x73, 0x65, 0x09, 0x6e, 0x61, 0x6d, 0x65];
248        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
249        let result = UserName::decode(ctx);
250        assert_eq!(
251            result.expect_err("Error expected"),
252            StunErrorType::InvalidParam
253        );
254
255        // Opaque string does not allow empty labels
256        let buffer: [u8; 0] = [];
257        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
258        let result = UserName::decode(ctx);
259        assert_eq!(
260            result.expect_err("Error expected"),
261            StunErrorType::InvalidParam
262        );
263
264        let value = "x".repeat(MAX_DECODED_SIZE + 1);
265        let ctx = AttributeDecoderContext::new(None, &dummy_msg, value.as_bytes());
266        assert_eq!(
267            UserName::decode(ctx).expect_err("Error expected"),
268            StunErrorType::ValueTooLong
269        );
270    }
271
272    #[test]
273    fn encode_user_name() {
274        let dummy_msg: [u8; 0] = [0x0; 0];
275        let user_name = UserName::try_from("username").expect("Can not create USERNAME attribute");
276
277        let mut buffer: [u8; 8] = [0x0; 8];
278        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
279        let result = user_name.encode(ctx);
280        assert_eq!(result, Ok(8));
281
282        let cmp_buffer = [0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65];
283        assert_eq!(&buffer[..], &cmp_buffer[..]);
284
285        let mut buffer: [u8; 1000] = [0x0; 1000];
286        let name = "x".repeat(MAX_ENCODED_SIZE - 1);
287
288        let user_name =
289            UserName::try_from(name.as_str()).expect("Can not create USERNAME attribute");
290
291        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
292        let result = user_name.encode(ctx);
293        assert_eq!(result, Ok(MAX_ENCODED_SIZE - 1));
294    }
295
296    #[test]
297    fn encode_user_name_error() {
298        let dummy_msg: [u8; 0] = [0x0; 0];
299
300        let user_name = UserName::try_from("username").expect("Can not create USERNAME attribute");
301
302        let mut buffer = [];
303        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
304        let result = user_name.encode(ctx);
305        assert_eq!(
306            result.expect_err("Error expected"),
307            StunErrorType::SmallBuffer
308        );
309
310        let mut buffer: [u8; 7] = [0x0; 7];
311        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
312        let result = user_name.encode(ctx);
313        assert_eq!(
314            result.expect_err("Error expected"),
315            StunErrorType::SmallBuffer
316        );
317
318        let mut buffer: [u8; MAX_ENCODED_SIZE] = [0x0; MAX_ENCODED_SIZE];
319        let value = "x".repeat(MAX_ENCODED_SIZE);
320        let user_name = UserName(value);
321        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
322        let result = user_name.encode(ctx);
323        assert_eq!(
324            result.expect_err("Error expected"),
325            StunErrorType::ValueTooLong
326        );
327    }
328
329    #[test]
330    fn user_name_stunt_attribute() {
331        let attr = StunAttribute::UserName(
332            UserName::try_from("test").expect("Can not create UserName attribute"),
333        );
334        assert!(attr.is_user_name());
335        assert!(attr.as_user_name().is_ok());
336        assert!(attr.as_unknown().is_err());
337
338        assert!(attr.attribute_type().is_comprehension_required());
339        assert!(!attr.attribute_type().is_comprehension_optional());
340
341        let dbg_fmt = format!("{:?}", attr);
342        assert_eq!("UserName(UserName(\"test\"))", dbg_fmt);
343    }
344}