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