stun_types/attribute/
user.rs1use std::convert::TryFrom;
10
11use crate::message::{StunParseError, StunWriteError};
12
13use super::{
14 Attribute, AttributeExt, AttributeStaticType, AttributeType, AttributeWrite, AttributeWriteExt,
15 RawAttribute,
16};
17
18#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct Username {
21 user: String,
22}
23impl AttributeStaticType for Username {
24 const TYPE: AttributeType = AttributeType(0x0006);
25}
26impl Attribute for Username {
27 fn get_type(&self) -> AttributeType {
28 Self::TYPE
29 }
30
31 fn length(&self) -> u16 {
32 self.user.len() as u16
33 }
34}
35impl AttributeWrite for Username {
36 fn to_raw(&self) -> RawAttribute {
37 RawAttribute::new(Username::TYPE, self.user.as_bytes())
38 }
39 fn write_into_unchecked(&self, dest: &mut [u8]) {
40 let len = self.padded_len();
41 self.write_header_unchecked(dest);
42 let offset = 4 + self.user.len();
43 dest[4..offset].copy_from_slice(self.user.as_bytes());
44 if len > offset {
45 dest[offset..len].fill(0);
46 }
47 }
48}
49impl<'a> TryFrom<&RawAttribute<'a>> for Username {
50 type Error = StunParseError;
51
52 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
53 raw.check_type_and_len(Self::TYPE, ..=513)?;
54 Ok(Self {
55 user: std::str::from_utf8(&raw.value)
56 .map_err(|_| StunParseError::InvalidAttributeData)?
57 .to_owned(),
58 })
59 }
60}
61
62impl Username {
63 pub fn new(user: &str) -> Result<Self, StunWriteError> {
79 if user.len() > 513 {
80 return Err(StunWriteError::TooLarge {
81 expected: 513,
82 actual: user.len(),
83 });
84 }
85 Ok(Self {
87 user: user.to_owned(),
88 })
89 }
90
91 pub fn username(&self) -> &str {
101 &self.user
102 }
103}
104
105impl std::fmt::Display for Username {
106 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 write!(f, "{}: '{}'", Self::TYPE, self.user)
108 }
109}
110
111#[derive(Debug, Clone, PartialEq, Eq)]
113pub struct Userhash {
114 hash: [u8; 32],
115}
116
117impl AttributeStaticType for Userhash {
118 const TYPE: AttributeType = AttributeType(0x001E);
119}
120impl Attribute for Userhash {
121 fn get_type(&self) -> AttributeType {
122 Self::TYPE
123 }
124
125 fn length(&self) -> u16 {
126 32
127 }
128}
129impl AttributeWrite for Userhash {
130 fn to_raw(&self) -> RawAttribute {
131 RawAttribute::new(Userhash::TYPE, &self.hash)
132 }
133
134 fn write_into_unchecked(&self, dest: &mut [u8]) {
135 self.write_header_unchecked(dest);
136 dest[4..4 + self.hash.len()].copy_from_slice(&self.hash);
137 }
138}
139
140impl<'a> TryFrom<&RawAttribute<'a>> for Userhash {
141 type Error = StunParseError;
142
143 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
144 raw.check_type_and_len(Self::TYPE, 32..=32)?;
145 let hash: [u8; 32] = raw.value[..32].try_into().unwrap();
147 Ok(Self { hash })
148 }
149}
150
151impl Userhash {
152 pub fn new(hash: [u8; 32]) -> Self {
163 Self { hash }
164 }
165
166 pub fn hash(&self) -> &[u8; 32] {
177 &self.hash
178 }
179
180 pub fn compute(user: &str, realm: &str) -> [u8; 32] {
188 let data = user.to_string() + ":" + realm;
189 use sha2::{Digest, Sha256};
190 let ret = Sha256::digest(data);
191 ret.as_slice().try_into().unwrap()
192 }
193}
194
195impl std::fmt::Display for Userhash {
196 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
197 write!(f, "{}: 0x", Self::TYPE)?;
198 for val in self.hash.iter() {
199 write!(f, "{:02x}", val)?;
200 }
201 Ok(())
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208 use byteorder::{BigEndian, ByteOrder};
209 use tracing::trace;
210
211 #[test]
212 fn username() {
213 let _log = crate::tests::test_init_log();
214 let s = "woohoo!";
215 let user = Username::new(s).unwrap();
216 trace!("{user}");
217 assert_eq!(user.username(), s);
218 assert_eq!(user.length() as usize, s.len());
219 let raw = RawAttribute::from(&user);
220 trace!("{raw}");
221 assert_eq!(raw.get_type(), Username::TYPE);
222 let user2 = Username::try_from(&raw).unwrap();
223 assert_eq!(user2.username(), s);
224 let mut data: Vec<_> = raw.into();
226 BigEndian::write_u16(&mut data[0..2], 0);
227 assert!(matches!(
228 Username::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
229 Err(StunParseError::WrongAttributeImplementation)
230 ));
231 }
232
233 #[test]
234 fn username_not_utf8() {
235 let _log = crate::tests::test_init_log();
236 let attr = Username::new("user").unwrap();
237 let raw = RawAttribute::from(&attr);
238 let mut data = raw.to_bytes();
239 data[6] = 0x88;
240 assert!(matches!(
241 Username::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
242 Err(StunParseError::InvalidAttributeData)
243 ));
244 }
245
246 #[test]
247 fn username_new_too_large() {
248 let _log = crate::tests::test_init_log();
249 let mut large = String::new();
250 for _i in 0..64 {
251 large.push_str("abcdefgh");
252 }
253 large.push_str("ab");
254 assert!(matches!(
255 Username::new(&large),
256 Err(StunWriteError::TooLarge {
257 expected: 513,
258 actual: 514
259 })
260 ));
261 }
262
263 #[test]
264 fn userhash() {
265 let _log = crate::tests::test_init_log();
266 let hash = Userhash::compute("username", "realm1");
267 let attr = Userhash::new(hash);
268 trace!("{attr}");
269 assert_eq!(attr.hash(), &hash);
270 assert_eq!(attr.length(), 32);
271 let raw = RawAttribute::from(&attr);
272 trace!("{raw}");
273 assert_eq!(raw.get_type(), Userhash::TYPE);
274 let mapped2 = Userhash::try_from(&raw).unwrap();
275 assert_eq!(mapped2.hash(), &hash);
276 let mut data: Vec<_> = raw.clone().into();
278 let len = data.len();
279 BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
280 assert!(matches!(
281 Userhash::try_from(&RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()),
282 Err(StunParseError::Truncated {
283 expected: 32,
284 actual: 31
285 })
286 ));
287 let mut data: Vec<_> = raw.into();
289 BigEndian::write_u16(&mut data[0..2], 0);
290 assert!(matches!(
291 Userhash::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
292 Err(StunParseError::WrongAttributeImplementation)
293 ));
294 }
295}