1use alloc::borrow::ToOwned;
10use alloc::string::String;
11use alloc::vec;
12use alloc::vec::Vec;
13use core::convert::TryFrom;
14
15use byteorder::{BigEndian, ByteOrder};
16
17use crate::message::{StunParseError, StunWriteError};
18
19use super::{
20 Attribute, AttributeExt, AttributeFromRaw, AttributeStaticType, AttributeType, AttributeWrite,
21 AttributeWriteExt, RawAttribute,
22};
23
24#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct ErrorCode {
27 code: u16,
28 reason: String,
29}
30impl AttributeStaticType for ErrorCode {
31 const TYPE: AttributeType = AttributeType(0x0009);
32}
33impl Attribute for ErrorCode {
34 fn get_type(&self) -> AttributeType {
35 Self::TYPE
36 }
37
38 fn length(&self) -> u16 {
39 self.reason.len() as u16 + 4
40 }
41}
42
43impl AttributeWrite for ErrorCode {
44 fn to_raw(&self) -> RawAttribute<'_> {
45 let mut data = Vec::with_capacity(self.length() as usize);
46 data.push(0u8);
47 data.push(0u8);
48 data.push((self.code / 100) as u8);
49 data.push((self.code % 100) as u8);
50 data.extend(self.reason.as_bytes());
51 RawAttribute::new_owned(ErrorCode::TYPE, data.into_boxed_slice())
52 }
53
54 fn write_into_unchecked(&self, dest: &mut [u8]) {
55 let len = self.padded_len();
56 let mut offset = self.write_header_unchecked(dest);
57 offset += self.write_into_data(&mut dest[offset..]);
58 if len - offset > 0 {
59 dest[offset..len].fill(0);
60 }
61 }
62}
63
64impl AttributeFromRaw<'_> for ErrorCode {
65 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
66 where
67 Self: Sized,
68 {
69 Self::try_from(raw)
70 }
71}
72
73impl TryFrom<&RawAttribute<'_>> for ErrorCode {
74 type Error = StunParseError;
75
76 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
77 raw.check_type_and_len(Self::TYPE, 4..=763 + 4)?;
78 let code_h = (raw.value[2] & 0x7) as u16;
79 let code_tens = raw.value[3] as u16;
80 if !(3..7).contains(&code_h) || code_tens > 99 {
81 return Err(StunParseError::InvalidAttributeData);
82 }
83 let code = code_h * 100 + code_tens;
84 Ok(Self {
85 code,
86 reason: core::str::from_utf8(&raw.value[4..])
87 .map_err(|_| StunParseError::InvalidAttributeData)?
88 .to_owned(),
89 })
90 }
91}
92
93#[derive(Debug)]
95pub struct ErrorCodeBuilder<'reason> {
96 code: u16,
97 reason: Option<&'reason str>,
98}
99
100impl<'reason> ErrorCodeBuilder<'reason> {
101 fn new(code: u16) -> Self {
102 Self { code, reason: None }
103 }
104
105 pub fn reason(mut self, reason: &'reason str) -> Self {
107 self.reason = Some(reason);
108 self
109 }
110
111 pub fn build(self) -> Result<ErrorCode, StunWriteError> {
117 if !(300..700).contains(&self.code) {
118 return Err(StunWriteError::OutOfRange {
119 value: self.code as usize,
120 min: 300,
121 max: 699,
122 });
123 }
124 let reason = self
125 .reason
126 .unwrap_or_else(|| ErrorCode::default_reason_for_code(self.code))
127 .to_owned();
128 Ok(ErrorCode {
129 code: self.code,
130 reason,
131 })
132 }
133}
134
135impl ErrorCode {
136 pub const TRY_ALTERNATE: u16 = 300;
141 pub const BAD_REQUEST: u16 = 400;
143 pub const UNAUTHORIZED: u16 = 401;
145 pub const FORBIDDEN: u16 = 403;
147 pub const UNKNOWN_ATTRIBUTE: u16 = 420;
150 pub const ALLOCATION_MISMATCH: u16 = 437;
152 pub const STALE_NONCE: u16 = 438;
154 pub const ADDRESS_FAMILY_NOT_SUPPORTED: u16 = 440;
156 pub const WRONG_CREDENTIALS: u16 = 441;
158 pub const UNSUPPORTED_TRANSPORT_PROTOCOL: u16 = 442;
160 pub const PEER_ADDRESS_FAMILY_MISMATCH: u16 = 443;
162 pub const CONNECTION_ALREADY_EXISTS: u16 = 446;
164 pub const CONNECTION_TIMEOUT_OR_FAILURE: u16 = 447;
166 pub const ALLOCATION_QUOTA_REACHED: u16 = 486;
168 pub const ROLE_CONFLICT: u16 = 487;
170 pub const SERVER_ERROR: u16 = 500;
172 pub const INSUFFICIENT_CAPACITY: u16 = 508;
174
175 pub fn builder<'reason>(code: u16) -> ErrorCodeBuilder<'reason> {
186 ErrorCodeBuilder::new(code)
187 }
188
189 pub fn new(code: u16, reason: &str) -> Result<Self, StunWriteError> {
204 if !(300..700).contains(&code) {
205 return Err(StunWriteError::OutOfRange {
206 value: code as usize,
207 min: 300,
208 max: 699,
209 });
210 }
211 Ok(Self {
212 code,
213 reason: reason.to_owned(),
214 })
215 }
216
217 pub fn code(&self) -> u16 {
227 self.code
228 }
229
230 pub fn reason(&self) -> &str {
240 &self.reason
241 }
242
243 pub fn default_reason_for_code(code: u16) -> &'static str {
268 match code {
269 Self::TRY_ALTERNATE => "Try Alternate",
270 Self::BAD_REQUEST => "Bad Request",
271 Self::UNAUTHORIZED => "Unauthorized",
272 Self::FORBIDDEN => "Forbidden",
273 Self::UNKNOWN_ATTRIBUTE => "Unknown Attribute",
274 Self::ALLOCATION_MISMATCH => "Allocation Mismatch",
275 Self::STALE_NONCE => "Stale Nonce",
276 Self::ADDRESS_FAMILY_NOT_SUPPORTED => "Address Family Not Supported",
277 Self::WRONG_CREDENTIALS => "Wrong Credentials",
278 Self::UNSUPPORTED_TRANSPORT_PROTOCOL => "Unsupported Transport Protocol",
279 Self::PEER_ADDRESS_FAMILY_MISMATCH => "Peer Address Family Mismatch",
280 Self::CONNECTION_ALREADY_EXISTS => "Connection Already Exists",
281 Self::CONNECTION_TIMEOUT_OR_FAILURE => "Connection Timeout or Failure",
282 Self::ALLOCATION_QUOTA_REACHED => "Allocation Quota Reached",
283 Self::ROLE_CONFLICT => "Role Conflict",
284 Self::SERVER_ERROR => "Server Error",
285 Self::INSUFFICIENT_CAPACITY => "Insufficient Capacity",
286 _ => "Unknown",
287 }
288 }
289
290 fn write_into_data(&self, dest: &mut [u8]) -> usize {
291 dest[0] = 0u8;
292 dest[1] = 0u8;
293 dest[2] = (self.code / 100) as u8;
294 dest[3] = (self.code % 100) as u8;
295 let bytes = self.reason.as_bytes();
296 dest[4..4 + bytes.len()].copy_from_slice(bytes);
297 4 + bytes.len()
298 }
299}
300
301impl core::fmt::Display for ErrorCode {
302 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
303 write!(f, "{}: {} '{}'", Self::TYPE, self.code, self.reason)
304 }
305}
306
307#[derive(Debug, Clone, PartialEq, Eq)]
309pub struct UnknownAttributes {
310 attributes: Vec<AttributeType>,
311}
312impl AttributeStaticType for UnknownAttributes {
313 const TYPE: AttributeType = AttributeType(0x000A);
314}
315impl Attribute for UnknownAttributes {
316 fn get_type(&self) -> AttributeType {
317 Self::TYPE
318 }
319 fn length(&self) -> u16 {
320 (self.attributes.len() as u16) * 2
321 }
322}
323impl AttributeWrite for UnknownAttributes {
324 fn to_raw(&self) -> RawAttribute<'_> {
325 let mut data = vec![0; self.length() as usize];
326 self.write_into_data(&mut data);
327 RawAttribute::new_owned(UnknownAttributes::TYPE, data.into_boxed_slice())
328 }
329
330 fn write_into_unchecked(&self, dest: &mut [u8]) {
331 let len = self.padded_len();
332 let mut offset = self.write_header_unchecked(dest);
333 offset += self.write_into_data(&mut dest[offset..]);
334 if len - offset > 0 {
335 dest[offset..len].fill(0);
336 }
337 }
338}
339
340impl AttributeFromRaw<'_> for UnknownAttributes {
341 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
342 where
343 Self: Sized,
344 {
345 Self::try_from(raw)
346 }
347}
348
349impl TryFrom<&RawAttribute<'_>> for UnknownAttributes {
350 type Error = StunParseError;
351
352 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
353 if raw.header.atype != Self::TYPE {
354 return Err(StunParseError::WrongAttributeImplementation);
355 }
356 if raw.value.len() % 2 != 0 {
357 return Err(StunParseError::Truncated {
359 expected: raw.value.len() + 1,
360 actual: raw.value.len(),
361 });
362 }
363 let mut attrs = vec![];
364 for attr in raw.value.chunks_exact(2) {
365 attrs.push(BigEndian::read_u16(attr).into());
366 }
367 Ok(Self { attributes: attrs })
368 }
369}
370impl UnknownAttributes {
371 pub fn new(attrs: &[AttributeType]) -> Self {
381 Self {
382 attributes: attrs.to_vec(),
383 }
384 }
385
386 pub fn add_attribute(&mut self, attr: AttributeType) {
397 if !self.has_attribute(attr) {
398 self.attributes.push(attr);
399 }
400 }
401
402 pub fn has_attribute(&self, attr: AttributeType) -> bool {
413 self.attributes.contains(&attr)
414 }
415
416 fn write_into_data(&self, dest: &mut [u8]) -> usize {
417 let mut offset = 0;
418 for attr in &self.attributes {
419 BigEndian::write_u16(&mut dest[offset..offset + 2], (*attr).into());
420 offset += 2;
421 }
422 offset
423 }
424}
425
426impl core::fmt::Display for UnknownAttributes {
427 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
428 write!(f, "{}: {:?}", Self::TYPE, self.attributes)
429 }
430}
431
432#[cfg(test)]
433mod tests {
434 use super::*;
435 use crate::attribute::{AlternateServer, Nonce, Realm};
436 use tracing::trace;
437
438 const CODES: [u16; 17] = [
439 300, 301, 400, 401, 403, 420, 437, 438, 440, 441, 442, 443, 486, 487, 500, 508, 699,
440 ];
441
442 #[test]
443 fn error_code() {
444 let _log = crate::tests::test_init_log();
445 for code in CODES {
446 let reason = ErrorCode::default_reason_for_code(code);
447 let err = ErrorCode::new(code, reason).unwrap();
448 trace!("{err}");
449 assert_eq!(err.code(), code);
450 assert_eq!(err.reason(), reason);
451 }
452 }
453
454 #[test]
455 fn error_code_raw() {
456 let _log = crate::tests::test_init_log();
457 for code in CODES {
458 let reason = ErrorCode::default_reason_for_code(code);
459 let err = ErrorCode::new(code, reason).unwrap();
460 let raw = RawAttribute::from(&err);
461 trace!("{raw}");
462 assert_eq!(raw.get_type(), ErrorCode::TYPE);
463 let err2 = ErrorCode::try_from(&raw).unwrap();
464 assert_eq!(err2.code(), code);
465 assert_eq!(err2.reason(), reason);
466 }
467 }
468
469 #[test]
470 fn error_code_write_into() {
471 let _log = crate::tests::test_init_log();
472 for code in CODES {
473 let reason = ErrorCode::default_reason_for_code(code);
474 let err = ErrorCode::new(code, reason).unwrap();
475 let raw = RawAttribute::from(&err);
476 let mut dest = vec![0; raw.padded_len()];
477 err.write_into(&mut dest).unwrap();
478 let raw = RawAttribute::from_bytes(&dest).unwrap();
479 let err2 = ErrorCode::try_from(&raw).unwrap();
480 assert_eq!(err2.code(), code);
481 assert_eq!(err2.reason(), reason);
482 }
483 }
484
485 #[test]
486 #[should_panic(expected = "out of range")]
487 fn error_code_write_into_unchecked() {
488 let _log = crate::tests::test_init_log();
489 let reason = ErrorCode::default_reason_for_code(CODES[0]);
490 let err = ErrorCode::new(CODES[0], reason).unwrap();
491 let raw = RawAttribute::from(&err);
492 let mut dest = vec![0; raw.padded_len() - 1];
493 err.write_into_unchecked(&mut dest);
494 }
495
496 fn error_code_new(code: u16) -> ErrorCode {
497 let reason = ErrorCode::default_reason_for_code(code);
498 ErrorCode::new(code, reason).unwrap()
499 }
500
501 #[test]
502 fn error_code_parse_short() {
503 let _log = crate::tests::test_init_log();
504 let err = error_code_new(420);
505 let raw = RawAttribute::from(&err);
506 let mut data: Vec<_> = raw.into();
508 let len = 0;
509 BigEndian::write_u16(&mut data[2..4], len as u16);
510 assert!(matches!(
511 ErrorCode::try_from(&RawAttribute::from_bytes(data[..len + 4].as_ref()).unwrap()),
512 Err(StunParseError::Truncated {
513 expected: 4,
514 actual: 0
515 })
516 ));
517 }
518
519 #[test]
520 fn error_code_parse_wrong_implementation() {
521 let _log = crate::tests::test_init_log();
522 let err = error_code_new(420);
523 let raw = RawAttribute::from(&err);
524 let mut data: Vec<_> = raw.into();
526 BigEndian::write_u16(&mut data[0..2], 0);
527 assert!(matches!(
528 ErrorCode::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
529 Err(StunParseError::WrongAttributeImplementation)
530 ));
531 }
532
533 #[test]
534 fn error_code_parse_out_of_range_code() {
535 let _log = crate::tests::test_init_log();
536 let err = error_code_new(420);
537 let raw = RawAttribute::from(&err);
538 let mut data: Vec<_> = raw.into();
539
540 data[6] = 7;
542 assert!(matches!(
543 ErrorCode::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
544 Err(StunParseError::InvalidAttributeData)
545 ));
546 }
547
548 #[test]
549 fn error_code_parse_invalid_reason() {
550 let _log = crate::tests::test_init_log();
551 let err = error_code_new(420);
552 let raw = RawAttribute::from(&err);
553 let mut data: Vec<_> = raw.into();
554
555 data[10] = 0x88;
557 assert!(matches!(
558 ErrorCode::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
559 Err(StunParseError::InvalidAttributeData)
560 ));
561 }
562
563 #[test]
564 fn error_code_build_default_reason() {
565 let _log = crate::tests::test_init_log();
566 let err = ErrorCode::builder(420).build().unwrap();
567 assert_eq!(err.code(), 420);
568 assert!(!err.reason().is_empty());
569 }
570
571 #[test]
572 fn error_code_build_out_of_range() {
573 let _log = crate::tests::test_init_log();
574 assert!(matches!(
575 ErrorCode::builder(700).build(),
576 Err(StunWriteError::OutOfRange {
577 value: 700,
578 min: _,
579 max: _
580 })
581 ));
582 }
583
584 #[test]
585 fn error_code_new_out_of_range() {
586 let _log = crate::tests::test_init_log();
587 assert!(matches!(
588 ErrorCode::new(700, "some-reason"),
589 Err(StunWriteError::OutOfRange {
590 value: 700,
591 min: _,
592 max: _
593 })
594 ));
595 }
596
597 #[test]
598 fn unknown_attributes() {
599 let _log = crate::tests::test_init_log();
600 let mut unknown = UnknownAttributes::new(&[Realm::TYPE]);
601 unknown.add_attribute(AlternateServer::TYPE);
602 unknown.add_attribute(AlternateServer::TYPE);
604 trace!("{unknown}");
605 assert!(unknown.has_attribute(Realm::TYPE));
606 assert!(unknown.has_attribute(AlternateServer::TYPE));
607 assert!(!unknown.has_attribute(Nonce::TYPE));
608 }
609
610 #[test]
611 fn unknown_attributes_raw() {
612 let _log = crate::tests::test_init_log();
613 let mut unknown = UnknownAttributes::new(&[Realm::TYPE]);
614 unknown.add_attribute(AlternateServer::TYPE);
615 let raw = RawAttribute::from(&unknown);
616 assert_eq!(raw.get_type(), UnknownAttributes::TYPE);
617 let unknown2 = UnknownAttributes::try_from(&raw).unwrap();
618 assert!(unknown2.has_attribute(Realm::TYPE));
619 assert!(unknown2.has_attribute(AlternateServer::TYPE));
620 assert!(!unknown2.has_attribute(Nonce::TYPE));
621 }
622
623 #[test]
624 fn unknown_attributes_raw_short() {
625 let _log = crate::tests::test_init_log();
626 let mut unknown = UnknownAttributes::new(&[Realm::TYPE]);
627 unknown.add_attribute(AlternateServer::TYPE);
628 let raw = RawAttribute::from(&unknown);
629 let mut data: Vec<_> = raw.clone().into();
631 let len = data.len();
632 BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
633 assert!(matches!(
634 UnknownAttributes::try_from(
635 &RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()
636 ),
637 Err(StunParseError::Truncated {
638 expected: 4,
639 actual: 3
640 })
641 ));
642 }
643
644 #[test]
645 fn unknown_attributes_raw_wrong_type() {
646 let _log = crate::tests::test_init_log();
647 let mut unknown = UnknownAttributes::new(&[Realm::TYPE]);
648 unknown.add_attribute(AlternateServer::TYPE);
649 let raw = RawAttribute::from(&unknown);
650 let mut data: Vec<_> = raw.clone().into();
652 BigEndian::write_u16(&mut data[0..2], 0);
653 assert!(matches!(
654 UnknownAttributes::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
655 Err(StunParseError::WrongAttributeImplementation)
656 ));
657 }
658
659 #[test]
660 fn unknown_attributes_write_into() {
661 let _log = crate::tests::test_init_log();
662 let mut unknown = UnknownAttributes::new(&[Realm::TYPE]);
663 unknown.add_attribute(AlternateServer::TYPE);
664 let raw = RawAttribute::from(&unknown);
665
666 let mut dest = vec![0; raw.padded_len()];
667 unknown.write_into(&mut dest).unwrap();
668 tracing::error!("{dest:?}");
669 let raw = RawAttribute::from_bytes(&dest).unwrap();
670 let unknown2 = UnknownAttributes::try_from(&raw).unwrap();
671 assert!(unknown2.has_attribute(Realm::TYPE));
672 assert!(unknown2.has_attribute(AlternateServer::TYPE));
673 }
674
675 #[test]
676 #[should_panic(expected = "out of range")]
677 fn unknown_attributes_write_into_unchecked() {
678 let _log = crate::tests::test_init_log();
679 let mut unknown = UnknownAttributes::new(&[Realm::TYPE]);
680 unknown.add_attribute(AlternateServer::TYPE);
681 let raw = RawAttribute::from(&unknown);
682
683 let mut dest = vec![0; raw.padded_len() - 1];
684 unknown.write_into_unchecked(&mut dest);
685 }
686}