stun_types/attribute/
user.rs1use 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#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct Username {
21 user: String,
22}
23
24impl AttributeStaticType for Username {
25 const TYPE: AttributeType = AttributeType(0x0006);
26}
27
28impl Attribute for Username {
29 fn get_type(&self) -> AttributeType {
30 Self::TYPE
31 }
32
33 fn length(&self) -> u16 {
34 self.user.len() as u16
35 }
36}
37
38impl AttributeWrite for Username {
39 fn to_raw(&self) -> RawAttribute<'_> {
40 RawAttribute::new(Username::TYPE, self.user.as_bytes())
41 }
42 fn write_into_unchecked(&self, dest: &mut [u8]) {
43 let len = self.padded_len();
44 self.write_header_unchecked(dest);
45 let offset = 4 + self.user.len();
46 dest[4..offset].copy_from_slice(self.user.as_bytes());
47 if len > offset {
48 dest[offset..len].fill(0);
49 }
50 }
51}
52
53impl AttributeFromRaw<'_> for Username {
54 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
55 where
56 Self: Sized,
57 {
58 Self::try_from(raw)
59 }
60}
61
62impl TryFrom<&RawAttribute<'_>> for Username {
63 type Error = StunParseError;
64
65 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
66 raw.check_type_and_len(Self::TYPE, ..=513)?;
67 Ok(Self {
68 user: std::str::from_utf8(&raw.value)
69 .map_err(|_| StunParseError::InvalidAttributeData)?
70 .to_owned(),
71 })
72 }
73}
74
75impl Username {
76 pub fn new(user: &str) -> Result<Self, StunWriteError> {
92 if user.len() > 513 {
93 return Err(StunWriteError::TooLarge {
94 expected: 513,
95 actual: user.len(),
96 });
97 }
98 Ok(Self {
100 user: user.to_owned(),
101 })
102 }
103
104 pub fn username(&self) -> &str {
114 &self.user
115 }
116}
117
118impl std::fmt::Display for Username {
119 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120 write!(f, "{}: '{}'", Self::TYPE, self.user)
121 }
122}
123
124#[derive(Debug, Clone, PartialEq, Eq)]
126pub struct Userhash {
127 hash: [u8; 32],
128}
129
130impl AttributeStaticType for Userhash {
131 const TYPE: AttributeType = AttributeType(0x001E);
132}
133
134impl Attribute for Userhash {
135 fn get_type(&self) -> AttributeType {
136 Self::TYPE
137 }
138
139 fn length(&self) -> u16 {
140 32
141 }
142}
143
144impl AttributeWrite for Userhash {
145 fn to_raw(&self) -> RawAttribute<'_> {
146 RawAttribute::new(Userhash::TYPE, &self.hash)
147 }
148
149 fn write_into_unchecked(&self, dest: &mut [u8]) {
150 self.write_header_unchecked(dest);
151 dest[4..4 + self.hash.len()].copy_from_slice(&self.hash);
152 }
153}
154
155impl AttributeFromRaw<'_> for Userhash {
156 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
157 where
158 Self: Sized,
159 {
160 Self::try_from(raw)
161 }
162}
163
164impl TryFrom<&RawAttribute<'_>> for Userhash {
165 type Error = StunParseError;
166
167 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
168 raw.check_type_and_len(Self::TYPE, 32..=32)?;
169 let hash: [u8; 32] = raw.value[..32].try_into().unwrap();
171 Ok(Self { hash })
172 }
173}
174
175impl Userhash {
176 pub fn new(hash: [u8; 32]) -> Self {
187 Self { hash }
188 }
189
190 pub fn hash(&self) -> &[u8; 32] {
201 &self.hash
202 }
203
204 pub fn compute(user: &str, realm: &str) -> [u8; 32] {
212 let data = user.to_string() + ":" + realm;
213 use sha2::{Digest, Sha256};
214 let ret = Sha256::digest(data);
215 ret.as_slice().try_into().unwrap()
216 }
217}
218
219impl std::fmt::Display for Userhash {
220 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
221 write!(f, "{}: 0x", Self::TYPE)?;
222 for val in self.hash.iter() {
223 write!(f, "{val:02x}")?;
224 }
225 Ok(())
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232 use byteorder::{BigEndian, ByteOrder};
233 use tracing::trace;
234
235 #[test]
236 fn username() {
237 let _log = crate::tests::test_init_log();
238 let s = "woohoo!";
239 let user = Username::new(s).unwrap();
240 trace!("{user}");
241 assert_eq!(user.username(), s);
242 assert_eq!(user.length() as usize, s.len());
243 let raw = RawAttribute::from(&user);
244 trace!("{raw}");
245 assert_eq!(raw.get_type(), Username::TYPE);
246 let user2 = Username::try_from(&raw).unwrap();
247 assert_eq!(user2.username(), s);
248 let mut data: Vec<_> = raw.into();
250 BigEndian::write_u16(&mut data[0..2], 0);
251 assert!(matches!(
252 Username::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
253 Err(StunParseError::WrongAttributeImplementation)
254 ));
255 }
256
257 #[test]
258 fn username_not_utf8() {
259 let _log = crate::tests::test_init_log();
260 let attr = Username::new("user").unwrap();
261 let raw = RawAttribute::from(&attr);
262 let mut data = raw.to_bytes();
263 data[6] = 0x88;
264 assert!(matches!(
265 Username::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
266 Err(StunParseError::InvalidAttributeData)
267 ));
268 }
269
270 #[test]
271 fn username_new_too_large() {
272 let _log = crate::tests::test_init_log();
273 let mut large = String::new();
274 for _i in 0..64 {
275 large.push_str("abcdefgh");
276 }
277 large.push_str("ab");
278 assert!(matches!(
279 Username::new(&large),
280 Err(StunWriteError::TooLarge {
281 expected: 513,
282 actual: 514
283 })
284 ));
285 }
286
287 #[test]
288 fn userhash() {
289 let _log = crate::tests::test_init_log();
290 let hash = Userhash::compute("username", "realm1");
291 let attr = Userhash::new(hash);
292 trace!("{attr}");
293 assert_eq!(attr.hash(), &hash);
294 assert_eq!(attr.length(), 32);
295 let raw = RawAttribute::from(&attr);
296 trace!("{raw}");
297 assert_eq!(raw.get_type(), Userhash::TYPE);
298 let mapped2 = Userhash::try_from(&raw).unwrap();
299 assert_eq!(mapped2.hash(), &hash);
300 let mut data: Vec<_> = raw.clone().into();
302 let len = data.len();
303 BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
304 assert!(matches!(
305 Userhash::try_from(&RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()),
306 Err(StunParseError::Truncated {
307 expected: 32,
308 actual: 31
309 })
310 ));
311 let mut data: Vec<_> = raw.into();
313 BigEndian::write_u16(&mut data[0..2], 0);
314 assert!(matches!(
315 Userhash::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
316 Err(StunParseError::WrongAttributeImplementation)
317 ));
318 }
319}