1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
51
52use hmac::{Hmac, Mac};
53use sha1::Sha1;
54use sipx_sdp::ice::{Credentials, Priority};
55use subtle::ConstantTimeEq as _;
56
57use sipx_transport::stun::{HEADER_LEN, MAGIC_COOKIE, is_stun};
58pub use sipx_transport::stun::{TransactionId, new_transaction_id};
59
60type HmacSha1 = Hmac<Sha1>;
61
62const METHOD_BINDING: u16 = 0x0001;
64
65const ATTR_USERNAME: u16 = 0x0006;
66const ATTR_MESSAGE_INTEGRITY: u16 = 0x0008;
67const ATTR_ERROR_CODE: u16 = 0x0009;
68const ATTR_XOR_MAPPED_ADDRESS: u16 = 0x0020;
69const ATTR_PRIORITY: u16 = 0x0024;
70const ATTR_USE_CANDIDATE: u16 = 0x0025;
71const ATTR_SOFTWARE: u16 = 0x8022;
72const ATTR_FINGERPRINT: u16 = 0x8028;
73const ATTR_ICE_CONTROLLED: u16 = 0x8029;
74const ATTR_ICE_CONTROLLING: u16 = 0x802a;
75
76const FAMILY_IPV4: u8 = 0x01;
77const FAMILY_IPV6: u8 = 0x02;
78
79const FINGERPRINT_XOR: u32 = 0x5354_554e;
82
83const PORT_KEY: u16 = 0x2112;
86
87const INTEGRITY_ATTR_LEN: usize = 24;
89const FINGERPRINT_ATTR_LEN: usize = 8;
91
92pub const ROLE_CONFLICT: u16 = 487;
94
95pub const ERROR_CODES: std::ops::RangeInclusive<u16> = 300..=699;
99
100const PAD: u8 = 0x20;
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
115#[non_exhaustive]
116pub enum Error {
117 #[error("not a STUN message")]
119 NotStun,
120 #[error("the STUN message ends inside what it claims to contain")]
122 Truncated,
123 #[error("STUN method {0:#06x} is not Binding")]
125 UnsupportedMethod(u16),
126 #[error("STUN attribute {0:#06x} is malformed")]
128 MalformedAttribute(u16),
129 #[error("STUN attribute {0:#06x} is computed by the encoder and cannot be supplied")]
135 ReservedAttribute(u16),
136 #[error("FINGERPRINT does not match the message")]
138 Fingerprint,
139 #[error("the message is longer than the STUN length field can describe")]
142 TooLong,
143}
144
145#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
147pub enum Class {
148 Request,
150 Indication,
152 Success,
154 Error,
156}
157
158impl Class {
159 const fn bits(self) -> u16 {
160 match self {
161 Self::Request => 0,
162 Self::Indication => 1,
163 Self::Success => 2,
164 Self::Error => 3,
165 }
166 }
167
168 const fn from_bits(bits: u16) -> Self {
169 match bits {
170 1 => Self::Indication,
171 2 => Self::Success,
172 3 => Self::Error,
173 _ => Self::Request,
174 }
175 }
176}
177
178const fn message_type(class: Class, method: u16) -> u16 {
181 let class = class.bits();
182 (method & 0x000f)
183 | ((method & 0x0070) << 1)
184 | ((method & 0x0f80) << 2)
185 | ((class & 0x1) << 4)
186 | ((class & 0x2) << 7)
187}
188
189const fn split_type(raw: u16) -> (Class, u16) {
191 let class = ((raw & 0x0100) >> 7) | ((raw & 0x0010) >> 4);
192 let method = (raw & 0x000f) | ((raw & 0x00e0) >> 1) | ((raw & 0x3e00) >> 2);
193 (Class::from_bits(class), method)
194}
195
196#[derive(Debug, Clone, PartialEq, Eq)]
208pub enum Attribute {
209 Username(String),
211 Priority(Priority),
218 UseCandidate,
220 IceControlled(u64),
222 IceControlling(u64),
224 ErrorCode {
232 code: u16,
234 reason: String,
236 },
237 XorMappedAddress(SocketAddr),
239 Software(String),
246 Unknown {
251 kind: u16,
253 value: Vec<u8>,
255 },
256}
257
258#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
265pub enum RoleAttribute {
266 Controlling {
268 tiebreaker: u64,
270 nominate: bool,
272 },
273 Controlled {
275 tiebreaker: u64,
277 },
278}
279
280impl RoleAttribute {
281 const fn attribute(self) -> Attribute {
282 match self {
283 Self::Controlling { tiebreaker, .. } => Attribute::IceControlling(tiebreaker),
284 Self::Controlled { tiebreaker } => Attribute::IceControlled(tiebreaker),
285 }
286 }
287
288 #[must_use]
290 pub const fn tiebreaker(self) -> u64 {
291 match self {
292 Self::Controlling { tiebreaker, .. } | Self::Controlled { tiebreaker } => tiebreaker,
293 }
294 }
295}
296
297#[derive(Debug, Clone, PartialEq, Eq)]
308pub struct Peering {
309 local: Credentials,
310 remote: Credentials,
311}
312
313impl Peering {
314 #[must_use]
316 pub const fn new(local: Credentials, remote: Credentials) -> Self {
317 Self { local, remote }
318 }
319
320 #[must_use]
322 pub const fn local(&self) -> &Credentials {
323 &self.local
324 }
325
326 #[must_use]
328 pub const fn remote(&self) -> &Credentials {
329 &self.remote
330 }
331
332 #[must_use]
334 pub fn outbound_username(&self) -> String {
335 format!("{}:{}", self.remote.ufrag(), self.local.ufrag())
336 }
337
338 #[must_use]
340 pub fn outbound_key(&self) -> &str {
341 self.remote.pwd()
342 }
343
344 #[must_use]
346 pub fn inbound_username(&self) -> String {
347 format!("{}:{}", self.local.ufrag(), self.remote.ufrag())
348 }
349
350 #[must_use]
352 pub fn inbound_key(&self) -> &str {
353 self.local.pwd()
354 }
355}
356
357#[derive(Debug, Clone, PartialEq, Eq)]
360pub struct Message {
361 class: Class,
362 transaction: TransactionId,
363 attributes: Vec<Attribute>,
364 integrity: Option<ReceivedIntegrity>,
365 fingerprint: bool,
366}
367
368#[derive(Debug, Clone, PartialEq, Eq)]
375struct ReceivedIntegrity {
376 tag: [u8; 20],
377 covered: Vec<u8>,
378}
379
380impl Message {
381 #[must_use]
383 pub const fn new(class: Class, transaction: TransactionId) -> Self {
384 Self {
385 class,
386 transaction,
387 attributes: Vec::new(),
388 integrity: None,
389 fingerprint: false,
390 }
391 }
392
393 #[must_use]
398 pub fn with(mut self, attribute: Attribute) -> Self {
399 self.attributes.push(attribute);
400 self
401 }
402
403 #[must_use]
405 pub const fn class(&self) -> Class {
406 self.class
407 }
408
409 #[must_use]
411 pub const fn transaction(&self) -> TransactionId {
412 self.transaction
413 }
414
415 #[must_use]
417 pub fn attributes(&self) -> &[Attribute] {
418 &self.attributes
419 }
420
421 pub fn encode(&self, key: Option<&str>) -> Result<Vec<u8>, Error> {
429 let mut out = Vec::with_capacity(HEADER_LEN + 64);
430 out.extend_from_slice(&message_type(self.class, METHOD_BINDING).to_be_bytes());
431 out.extend_from_slice(&0u16.to_be_bytes());
433 out.extend_from_slice(&MAGIC_COOKIE.to_be_bytes());
434 out.extend_from_slice(&self.transaction);
435 for attribute in &self.attributes {
436 attribute.encode_into(&mut out, &self.transaction)?;
437 }
438 if let Some(key) = key {
439 set_length(&mut out, INTEGRITY_ATTR_LEN)?;
441 let tag = hmac_sha1(key.as_bytes(), &out);
442 push_attribute(&mut out, ATTR_MESSAGE_INTEGRITY, &tag)?;
443 }
444 set_length(&mut out, FINGERPRINT_ATTR_LEN)?;
447 let crc = crc32(&out) ^ FINGERPRINT_XOR;
448 push_attribute(&mut out, ATTR_FINGERPRINT, &crc.to_be_bytes())?;
449 Ok(out)
450 }
451
452 pub fn decode(datagram: &[u8]) -> Result<Self, Error> {
460 if !is_stun(datagram) {
461 return Err(Error::NotStun);
462 }
463 let (class, method) = split_type(read_u16(datagram, 0).ok_or(Error::Truncated)?);
464 if method != METHOD_BINDING {
465 return Err(Error::UnsupportedMethod(method));
466 }
467 let transaction: TransactionId = datagram
468 .get(8..HEADER_LEN)
469 .and_then(|bytes| <[u8; 12]>::try_from(bytes).ok())
470 .ok_or(Error::Truncated)?;
471 let stated = usize::from(read_u16(datagram, 2).ok_or(Error::Truncated)?);
474 let end = HEADER_LEN.checked_add(stated).ok_or(Error::Truncated)?;
475 let body = datagram.get(HEADER_LEN..end).ok_or(Error::Truncated)?;
476
477 let mut message = Self::new(class, transaction);
478 let mut offset = 0usize;
479 while offset < body.len() {
480 let kind = read_u16(body, offset).ok_or(Error::Truncated)?;
481 let length = usize::from(
482 read_u16(body, offset.checked_add(2).ok_or(Error::Truncated)?)
483 .ok_or(Error::Truncated)?,
484 );
485 let start = offset.checked_add(4).ok_or(Error::Truncated)?;
486 let value = start
487 .checked_add(length)
488 .and_then(|end| body.get(start..end))
489 .ok_or(Error::Truncated)?;
490
491 match kind {
492 ATTR_MESSAGE_INTEGRITY if message.integrity.is_none() => {
493 message.integrity = Some(ReceivedIntegrity {
494 tag: <[u8; 20]>::try_from(value)
495 .map_err(|_| Error::MalformedAttribute(kind))?,
496 covered: covered_prefix(datagram, offset, INTEGRITY_ATTR_LEN)?,
497 });
498 }
499 ATTR_FINGERPRINT => {
500 if value.len() != 4 {
501 return Err(Error::MalformedAttribute(kind));
502 }
503 let stated_crc = read_u32(value, 0).ok_or(Error::MalformedAttribute(kind))?;
504 let prefix = covered_prefix(datagram, offset, FINGERPRINT_ATTR_LEN)?;
505 if crc32(&prefix) ^ FINGERPRINT_XOR != stated_crc {
506 return Err(Error::Fingerprint);
507 }
508 message.fingerprint = true;
509 break;
512 }
513 _ if message.integrity.is_some() => {
514 }
519 _ => message
520 .attributes
521 .push(Attribute::decode(kind, value, &transaction)?),
522 }
523
524 let padded = length.checked_add(3).ok_or(Error::Truncated)? & !3;
527 offset = start.checked_add(padded).ok_or(Error::Truncated)?;
528 }
529 Ok(message)
530 }
531
532 #[must_use]
540 pub fn verify_integrity(&self, key: &str) -> bool {
541 let Some(integrity) = self.integrity.as_ref() else {
542 return false;
543 };
544 let computed = hmac_sha1(key.as_bytes(), &integrity.covered);
545 computed.ct_eq(&integrity.tag).into()
550 }
551
552 #[must_use]
554 pub const fn has_integrity(&self) -> bool {
555 self.integrity.is_some()
556 }
557
558 #[must_use]
561 pub const fn has_fingerprint(&self) -> bool {
562 self.fingerprint
563 }
564
565 #[must_use]
567 pub fn username(&self) -> Option<&str> {
568 self.attributes
569 .iter()
570 .find_map(|attribute| match attribute {
571 Attribute::Username(name) => Some(name.as_str()),
572 _ => None,
573 })
574 }
575
576 #[must_use]
578 pub fn priority(&self) -> Option<Priority> {
579 self.attributes
580 .iter()
581 .find_map(|attribute| match attribute {
582 Attribute::Priority(priority) => Some(*priority),
583 _ => None,
584 })
585 }
586
587 #[must_use]
589 pub fn use_candidate(&self) -> bool {
590 self.attributes
591 .iter()
592 .any(|attribute| matches!(attribute, Attribute::UseCandidate))
593 }
594
595 #[must_use]
602 pub fn role(&self) -> Option<RoleAttribute> {
603 self.attributes
604 .iter()
605 .find_map(|attribute| match attribute {
606 Attribute::IceControlling(tiebreaker) => Some(RoleAttribute::Controlling {
607 tiebreaker: *tiebreaker,
608 nominate: self.use_candidate(),
609 }),
610 Attribute::IceControlled(tiebreaker) => Some(RoleAttribute::Controlled {
611 tiebreaker: *tiebreaker,
612 }),
613 _ => None,
614 })
615 }
616
617 #[must_use]
619 pub fn error_code(&self) -> Option<u16> {
620 self.attributes
621 .iter()
622 .find_map(|attribute| match attribute {
623 Attribute::ErrorCode { code, .. } => Some(*code),
624 _ => None,
625 })
626 }
627
628 #[must_use]
630 pub fn mapped_address(&self) -> Option<SocketAddr> {
631 self.attributes
632 .iter()
633 .find_map(|attribute| match attribute {
634 Attribute::XorMappedAddress(address) => Some(*address),
635 _ => None,
636 })
637 }
638}
639
640impl Attribute {
641 fn encode_into(&self, out: &mut Vec<u8>, transaction: &TransactionId) -> Result<(), Error> {
642 let (kind, value) = match self {
643 Self::Username(name) => (ATTR_USERNAME, name.as_bytes().to_vec()),
644 Self::Priority(priority) => (ATTR_PRIORITY, priority.get().to_be_bytes().to_vec()),
645 Self::UseCandidate => (ATTR_USE_CANDIDATE, Vec::new()),
646 Self::IceControlled(tiebreaker) => {
647 (ATTR_ICE_CONTROLLED, tiebreaker.to_be_bytes().to_vec())
648 }
649 Self::IceControlling(tiebreaker) => {
650 (ATTR_ICE_CONTROLLING, tiebreaker.to_be_bytes().to_vec())
651 }
652 Self::ErrorCode { code, reason } => {
653 (ATTR_ERROR_CODE, encode_error_code(*code, reason)?)
654 }
655 Self::XorMappedAddress(address) => (
656 ATTR_XOR_MAPPED_ADDRESS,
657 encode_xor_mapped(*address, transaction),
658 ),
659 Self::Software(text) => (ATTR_SOFTWARE, text.as_bytes().to_vec()),
660 Self::Unknown { kind, value } => {
661 if matches!(*kind, ATTR_MESSAGE_INTEGRITY | ATTR_FINGERPRINT) {
662 return Err(Error::ReservedAttribute(*kind));
663 }
664 (*kind, value.clone())
665 }
666 };
667 push_attribute(out, kind, &value)
668 }
669
670 fn decode(kind: u16, value: &[u8], transaction: &TransactionId) -> Result<Self, Error> {
671 let malformed = || Error::MalformedAttribute(kind);
672 Ok(match kind {
673 ATTR_USERNAME => Self::Username(text(value, kind)?),
674 ATTR_SOFTWARE => Self::Software(text(value, kind)?),
675 ATTR_PRIORITY => {
676 let raw = fixed_u32(value, kind)?;
677 Self::Priority(Priority::new(raw).ok_or_else(malformed)?)
681 }
682 ATTR_USE_CANDIDATE => {
683 if !value.is_empty() {
684 return Err(malformed());
685 }
686 Self::UseCandidate
687 }
688 ATTR_ICE_CONTROLLED => Self::IceControlled(fixed_u64(value, kind)?),
689 ATTR_ICE_CONTROLLING => Self::IceControlling(fixed_u64(value, kind)?),
690 ATTR_ERROR_CODE => {
691 let class = u16::from(*value.get(2).ok_or_else(malformed)? & 0x07);
692 let number = u16::from(*value.get(3).ok_or_else(malformed)?);
693 let code = class
694 .checked_mul(100)
695 .and_then(|hundreds| hundreds.checked_add(number))
696 .filter(|code| ERROR_CODES.contains(code))
697 .ok_or_else(malformed)?;
698 Self::ErrorCode {
699 code,
700 reason: text(value.get(4..).unwrap_or_default(), kind)?,
701 }
702 }
703 ATTR_XOR_MAPPED_ADDRESS => {
704 Self::XorMappedAddress(decode_xor_mapped(value, transaction).ok_or_else(malformed)?)
705 }
706 _ => Self::Unknown {
707 kind,
708 value: value.to_vec(),
709 },
710 })
711 }
712}
713
714fn text(value: &[u8], kind: u16) -> Result<String, Error> {
717 std::str::from_utf8(value)
718 .map(str::to_owned)
719 .map_err(|_| Error::MalformedAttribute(kind))
720}
721
722fn fixed_u32(value: &[u8], kind: u16) -> Result<u32, Error> {
724 <[u8; 4]>::try_from(value)
725 .map(u32::from_be_bytes)
726 .map_err(|_| Error::MalformedAttribute(kind))
727}
728
729fn fixed_u64(value: &[u8], kind: u16) -> Result<u64, Error> {
731 <[u8; 8]>::try_from(value)
732 .map(u64::from_be_bytes)
733 .map_err(|_| Error::MalformedAttribute(kind))
734}
735
736fn read_u16(bytes: &[u8], at: usize) -> Option<u16> {
738 let end = at.checked_add(2)?;
739 <[u8; 2]>::try_from(bytes.get(at..end)?)
740 .ok()
741 .map(u16::from_be_bytes)
742}
743
744fn read_u32(bytes: &[u8], at: usize) -> Option<u32> {
746 let end = at.checked_add(4)?;
747 <[u8; 4]>::try_from(bytes.get(at..end)?)
748 .ok()
749 .map(u32::from_be_bytes)
750}
751
752fn covered_prefix(datagram: &[u8], offset: usize, attr_len: usize) -> Result<Vec<u8>, Error> {
758 let end = HEADER_LEN.checked_add(offset).ok_or(Error::Truncated)?;
759 let mut prefix = datagram.get(..end).ok_or(Error::Truncated)?.to_vec();
760 let body = offset.checked_add(attr_len).ok_or(Error::Truncated)?;
761 let length = u16::try_from(body).map_err(|_| Error::Truncated)?;
762 prefix
763 .get_mut(2..4)
764 .ok_or(Error::Truncated)?
765 .copy_from_slice(&length.to_be_bytes());
766 Ok(prefix)
767}
768
769fn decode_xor_mapped(value: &[u8], transaction: &TransactionId) -> Option<SocketAddr> {
771 let family = *value.get(1)?;
772 let port = read_u16(value, 2)? ^ PORT_KEY;
773 match family {
774 FAMILY_IPV4 => {
775 let raw = read_u32(value, 4)?;
776 let address = Ipv4Addr::from(raw ^ MAGIC_COOKIE);
777 Some(SocketAddr::new(IpAddr::V4(address), port))
778 }
779 FAMILY_IPV6 => {
780 let raw = <[u8; 16]>::try_from(value.get(4..20)?).ok()?;
781 let key = xor_key(transaction);
782 let mut octets = [0u8; 16];
783 for (slot, (byte, k)) in octets.iter_mut().zip(raw.into_iter().zip(key)) {
784 *slot = byte ^ k;
785 }
786 Some(SocketAddr::new(IpAddr::V6(Ipv6Addr::from(octets)), port))
787 }
788 _ => None,
789 }
790}
791
792fn hmac_sha1(key: &[u8], data: &[u8]) -> [u8; 20] {
799 let mut mac = <HmacSha1 as Mac>::new_from_slice(key)
800 .unwrap_or_else(|_| unreachable!("HMAC accepts a key of any length"));
801 mac.update(data);
802 let full = mac.finalize().into_bytes();
803 let mut tag = [0u8; 20];
804 for (slot, byte) in tag.iter_mut().zip(full) {
805 *slot = byte;
806 }
807 tag
808}
809
810fn crc32(data: &[u8]) -> u32 {
817 const POLYNOMIAL: u32 = 0xedb8_8320;
819 let mut crc = 0xffff_ffff_u32;
820 for byte in data {
821 crc ^= u32::from(*byte);
822 for _ in 0..8 {
823 crc = if crc & 1 == 1 {
824 (crc >> 1) ^ POLYNOMIAL
825 } else {
826 crc >> 1
827 };
828 }
829 }
830 !crc
831}
832
833fn push_attribute(out: &mut Vec<u8>, kind: u16, value: &[u8]) -> Result<(), Error> {
835 let stated = u16::try_from(value.len()).map_err(|_| Error::TooLong)?;
836 out.extend_from_slice(&kind.to_be_bytes());
837 out.extend_from_slice(&stated.to_be_bytes());
838 out.extend_from_slice(value);
839 out.extend(std::iter::repeat_n(PAD, (4 - value.len() % 4) % 4));
840 Ok(())
841}
842
843fn set_length(out: &mut [u8], extra: usize) -> Result<(), Error> {
851 let body = out
852 .len()
853 .checked_sub(HEADER_LEN)
854 .and_then(|written| written.checked_add(extra))
855 .ok_or(Error::TooLong)?;
856 let length = u16::try_from(body).map_err(|_| Error::TooLong)?;
857 out.get_mut(2..4)
858 .ok_or(Error::TooLong)?
859 .copy_from_slice(&length.to_be_bytes());
860 Ok(())
861}
862
863fn encode_error_code(code: u16, reason: &str) -> Result<Vec<u8>, Error> {
872 if !ERROR_CODES.contains(&code) {
873 return Err(Error::MalformedAttribute(ATTR_ERROR_CODE));
874 }
875 let class = u8::try_from(code / 100).unwrap_or_default();
876 let number = u8::try_from(code % 100).unwrap_or_default();
877 let mut value = vec![0, 0, class, number];
878 value.extend_from_slice(reason.as_bytes());
879 Ok(value)
880}
881
882fn xor_key(transaction: &TransactionId) -> [u8; 16] {
884 let mut key = [0u8; 16];
885 let source = MAGIC_COOKIE
886 .to_be_bytes()
887 .into_iter()
888 .chain(transaction.iter().copied());
889 for (slot, byte) in key.iter_mut().zip(source) {
890 *slot = byte;
891 }
892 key
893}
894
895fn encode_xor_mapped(address: SocketAddr, transaction: &TransactionId) -> Vec<u8> {
900 let mut value = Vec::with_capacity(20);
901 value.push(0);
902 match address.ip() {
903 IpAddr::V4(v4) => {
904 value.push(FAMILY_IPV4);
905 value.extend_from_slice(&(address.port() ^ PORT_KEY).to_be_bytes());
906 let raw = u32::from_be_bytes(v4.octets());
907 value.extend_from_slice(&(raw ^ MAGIC_COOKIE).to_be_bytes());
908 }
909 IpAddr::V6(v6) => {
910 value.push(FAMILY_IPV6);
911 value.extend_from_slice(&(address.port() ^ PORT_KEY).to_be_bytes());
912 let key = xor_key(transaction);
913 value.extend(v6.octets().into_iter().zip(key).map(|(byte, k)| byte ^ k));
914 }
915 }
916 value
917}
918
919pub fn connectivity_check(
925 transaction: TransactionId,
926 peering: &Peering,
927 priority: Priority,
928 role: RoleAttribute,
929) -> Result<Vec<u8>, Error> {
930 let mut message = Message::new(Class::Request, transaction)
931 .with(Attribute::Priority(priority))
932 .with(role.attribute())
933 .with(Attribute::Username(peering.outbound_username()));
934 if matches!(role, RoleAttribute::Controlling { nominate: true, .. }) {
935 message = message.with(Attribute::UseCandidate);
936 }
937 message.encode(Some(peering.outbound_key()))
938}
939
940pub fn check_success(
949 transaction: TransactionId,
950 peering: &Peering,
951 mapped: SocketAddr,
952) -> Result<Vec<u8>, Error> {
953 Message::new(Class::Success, transaction)
954 .with(Attribute::XorMappedAddress(mapped))
955 .encode(Some(peering.inbound_key()))
956}
957
958pub fn role_conflict(transaction: TransactionId, peering: &Peering) -> Result<Vec<u8>, Error> {
960 Message::new(Class::Error, transaction)
961 .with(Attribute::ErrorCode {
962 code: ROLE_CONFLICT,
963 reason: "Role Conflict".to_owned(),
964 })
965 .encode(Some(peering.inbound_key()))
966}
967
968pub fn keepalive(transaction: TransactionId) -> Result<Vec<u8>, Error> {
975 Message::new(Class::Indication, transaction).encode(None)
976}
977
978#[cfg(test)]
979#[allow(
980 clippy::unwrap_used,
981 clippy::expect_used,
982 clippy::panic,
983 clippy::indexing_slicing
984)]
985mod tests {
986 use super::*;
987
988 fn hex(text: &str) -> Vec<u8> {
991 text.split_whitespace()
992 .map(|byte| u8::from_str_radix(byte, 16).expect("a hex byte"))
993 .collect()
994 }
995
996 const SAMPLE_REQUEST: &str = "
999 00 01 00 58 21 12 a4 42 b7 e7 a7 01 bc 34 d6 86
1000 fa 87 df ae 80 22 00 10 53 54 55 4e 20 74 65 73
1001 74 20 63 6c 69 65 6e 74 00 24 00 04 6e 00 01 ff
1002 80 29 00 08 93 2f f9 b1 51 26 3b 36 00 06 00 09
1003 65 76 74 6a 3a 68 36 76 59 20 20 20 00 08 00 14
1004 9a ea a7 0c bf d8 cb 56 78 1e f2 b5 b2 d3 f2 49
1005 c1 b5 71 a2 80 28 00 04 e5 7a 3b cf";
1006
1007 const SAMPLE_ID: TransactionId = [
1008 0xb7, 0xe7, 0xa7, 0x01, 0xbc, 0x34, 0xd6, 0x86, 0xfa, 0x87, 0xdf, 0xae,
1009 ];
1010
1011 const SAMPLE_SOFTWARE: &str = "STUN test client";
1013 const SAMPLE_UFRAG_SENDER: &str = "h6vY";
1014 const SAMPLE_UFRAG_RECEIVER: &str = "evtj";
1015 const SAMPLE_PASSWORD: &str = "VOkJxbRl1RmTxUk/WvJxBt";
1016 const SAMPLE_PRIORITY: u32 = 0x6e00_01ff;
1017 const SAMPLE_TIEBREAKER: u64 = 0x932f_f9b1_5126_3b36;
1018
1019 const OTHER_PASSWORD: &str = "aPasswordTheRfcNeverStates";
1023
1024 fn sample_sender() -> Peering {
1028 Peering::new(
1029 Credentials::new(SAMPLE_UFRAG_SENDER, OTHER_PASSWORD).expect("valid credentials"),
1030 Credentials::received(SAMPLE_UFRAG_RECEIVER, SAMPLE_PASSWORD)
1031 .expect("valid credentials"),
1032 )
1033 }
1034
1035 fn sample_receiver() -> Peering {
1039 Peering::new(
1040 Credentials::new(SAMPLE_UFRAG_RECEIVER, SAMPLE_PASSWORD).expect("valid credentials"),
1041 Credentials::received(SAMPLE_UFRAG_SENDER, OTHER_PASSWORD).expect("valid credentials"),
1042 )
1043 }
1044
1045 fn sample_priority() -> Priority {
1046 Priority::new(SAMPLE_PRIORITY).expect("in range")
1047 }
1048
1049 const REQUEST_ICE_ATTRIBUTES: std::ops::Range<usize> = 40..76;
1052 const REQUEST_INTEGRITY_TAG: std::ops::Range<usize> = 80..100;
1053 const REQUEST_INTEGRITY_BODY_OFFSET: u16 = 56;
1055
1056 #[test]
1064 fn a_connectivity_check_encodes_to_the_rfc_5769_sample_request() {
1065 let peering = sample_sender();
1066
1067 let message = Message::new(Class::Request, SAMPLE_ID)
1068 .with(Attribute::Software(SAMPLE_SOFTWARE.to_owned()))
1069 .with(Attribute::Priority(sample_priority()))
1070 .with(Attribute::IceControlled(SAMPLE_TIEBREAKER))
1071 .with(Attribute::Username(peering.outbound_username()));
1072
1073 assert_eq!(
1074 message
1075 .encode(Some(peering.outbound_key()))
1076 .expect("encodes"),
1077 hex(SAMPLE_REQUEST),
1078 "the encoder does not reproduce RFC 5769 §2.1"
1079 );
1080
1081 let check = connectivity_check(
1085 SAMPLE_ID,
1086 &peering,
1087 sample_priority(),
1088 RoleAttribute::Controlled {
1089 tiebreaker: SAMPLE_TIEBREAKER,
1090 },
1091 )
1092 .expect("encodes");
1093 let vector = hex(SAMPLE_REQUEST);
1094 assert_eq!(
1095 &check[HEADER_LEN..HEADER_LEN + REQUEST_ICE_ATTRIBUTES.len()],
1096 &vector[REQUEST_ICE_ATTRIBUTES],
1097 );
1098 }
1099
1100 #[test]
1107 fn a_success_response_encodes_to_the_rfc_5769_sample_response() {
1108 const SAMPLE_RESPONSE: &str = "
1109 01 01 00 3c 21 12 a4 42 b7 e7 a7 01 bc 34 d6 86
1110 fa 87 df ae 80 22 00 0b 74 65 73 74 20 76 65 63
1111 74 6f 72 20 00 20 00 08 00 01 a1 47 e1 12 a6 43
1112 00 08 00 14 2b 91 f5 99 fd 9e 90 c3 8c 74 89 f9
1113 2a f9 ba 53 f0 6b e7 d7 80 28 00 04 c0 7d 4c 96";
1114 const MAPPED: &str = "192.0.2.1:32853";
1117 const RESPONSE_MAPPED_ADDRESS: std::ops::Range<usize> = 36..48;
1118
1119 let peering = sample_receiver();
1120 let mapped: SocketAddr = MAPPED.parse().expect("valid");
1121
1122 let message = Message::new(Class::Success, SAMPLE_ID)
1123 .with(Attribute::Software("test vector".to_owned()))
1124 .with(Attribute::XorMappedAddress(mapped));
1125
1126 assert_eq!(
1127 message
1128 .encode(Some(peering.inbound_key()))
1129 .expect("encodes"),
1130 hex(SAMPLE_RESPONSE),
1131 "the encoder does not reproduce RFC 5769 §2.2"
1132 );
1133
1134 let response = check_success(SAMPLE_ID, &peering, mapped).expect("encodes");
1135 let vector = hex(SAMPLE_RESPONSE);
1136 assert_eq!(
1137 &response[HEADER_LEN..HEADER_LEN + RESPONSE_MAPPED_ADDRESS.len()],
1138 &vector[RESPONSE_MAPPED_ADDRESS],
1139 );
1140 }
1141
1142 #[test]
1150 fn the_integrity_is_taken_over_the_adjusted_length_and_not_the_real_one() {
1151 let vector = hex(SAMPLE_REQUEST);
1152 assert_eq!(vector.len(), 108, "RFC 5769 §2.1 is 108 bytes");
1153 assert_eq!(&vector[2..4], &[0x00, 0x58], "the vector's real length, 88");
1154
1155 let mut covered = vector[..76].to_vec();
1156 let real = hmac_sha1(SAMPLE_PASSWORD.as_bytes(), &covered);
1157 assert_ne!(
1158 &real[..],
1159 &vector[REQUEST_INTEGRITY_TAG],
1160 "the real length must not produce the published tag"
1161 );
1162
1163 let adjusted = REQUEST_INTEGRITY_BODY_OFFSET + 24;
1164 assert_eq!(adjusted, 80);
1165 covered[2..4].copy_from_slice(&adjusted.to_be_bytes());
1166 assert_eq!(
1167 &hmac_sha1(SAMPLE_PASSWORD.as_bytes(), &covered)[..],
1168 &vector[REQUEST_INTEGRITY_TAG],
1169 "the adjusted length must"
1170 );
1171 }
1172
1173 #[test]
1176 fn the_integrity_comes_before_the_fingerprint_and_both_come_last() {
1177 let peering = sample_sender();
1178 let check = connectivity_check(
1179 SAMPLE_ID,
1180 &peering,
1181 sample_priority(),
1182 RoleAttribute::Controlling {
1183 tiebreaker: SAMPLE_TIEBREAKER,
1184 nominate: true,
1185 },
1186 )
1187 .expect("encodes");
1188
1189 let integrity = check.len() - INTEGRITY_ATTR_LEN - FINGERPRINT_ATTR_LEN;
1190 let fingerprint = check.len() - FINGERPRINT_ATTR_LEN;
1191 assert_eq!(
1192 read_u16(&check, integrity),
1193 Some(ATTR_MESSAGE_INTEGRITY),
1194 "MESSAGE-INTEGRITY is second to last"
1195 );
1196 assert_eq!(
1197 read_u16(&check, fingerprint),
1198 Some(ATTR_FINGERPRINT),
1199 "FINGERPRINT is last"
1200 );
1201
1202 let expected = crc32(&check[..fingerprint]) ^ FINGERPRINT_XOR;
1205 assert_eq!(read_u32(&check, fingerprint + 4), Some(expected));
1206
1207 let decoded = Message::decode(&check).expect("our own check decodes");
1208 assert!(decoded.has_integrity() && decoded.has_fingerprint());
1209 assert!(decoded.verify_integrity(peering.outbound_key()));
1210 }
1211
1212 #[test]
1218 fn the_username_and_key_of_a_check_depend_on_which_way_it_travels() {
1219 let sender = sample_sender();
1220 let receiver = sample_receiver();
1221
1222 assert_eq!(sender.outbound_username(), "evtj:h6vY");
1223 assert_eq!(sender.outbound_key(), SAMPLE_PASSWORD);
1224 assert_eq!(receiver.inbound_username(), "evtj:h6vY");
1225 assert_eq!(receiver.inbound_key(), SAMPLE_PASSWORD);
1226 assert_eq!(sender.inbound_username(), "h6vY:evtj");
1227 assert_eq!(sender.inbound_key(), OTHER_PASSWORD);
1228 assert_eq!(receiver.outbound_username(), "h6vY:evtj");
1229 assert_eq!(receiver.outbound_key(), OTHER_PASSWORD);
1230
1231 let arrived = Message::decode(&hex(SAMPLE_REQUEST)).expect("decodes");
1233 assert_eq!(
1234 arrived.username(),
1235 Some(receiver.inbound_username()).as_deref()
1236 );
1237 assert!(
1238 arrived.verify_integrity(receiver.inbound_key()),
1239 "a check that arrived is keyed with our password"
1240 );
1241 assert!(
1242 !arrived.verify_integrity(receiver.outbound_key()),
1243 "keying an inbound check with the peer's password answers nothing and looks like a \
1244 network fault"
1245 );
1246 }
1247
1248 #[test]
1250 fn every_profile_attribute_encodes_as_well_as_decodes() {
1251 let attributes = vec![
1252 Attribute::Username("evtj:h6vY".to_owned()),
1253 Attribute::Priority(sample_priority()),
1254 Attribute::UseCandidate,
1255 Attribute::IceControlling(SAMPLE_TIEBREAKER),
1256 Attribute::ErrorCode {
1257 code: ROLE_CONFLICT,
1258 reason: "Role Conflict".to_owned(),
1259 },
1260 Attribute::XorMappedAddress("192.0.2.1:32853".parse().expect("valid")),
1261 Attribute::Software(SAMPLE_SOFTWARE.to_owned()),
1262 Attribute::Unknown {
1263 kind: 0x8050,
1264 value: vec![1, 2, 3],
1265 },
1266 ];
1267 let message = attributes
1268 .iter()
1269 .cloned()
1270 .fold(Message::new(Class::Request, SAMPLE_ID), Message::with);
1271 let bytes = message.encode(Some(SAMPLE_PASSWORD)).expect("encodes");
1272 let decoded = Message::decode(&bytes).expect("decodes");
1273
1274 assert_eq!(decoded.attributes(), attributes.as_slice());
1275 assert_eq!(decoded.class(), Class::Request);
1276 assert_eq!(decoded.transaction(), SAMPLE_ID);
1277 assert_eq!(decoded.error_code(), Some(487));
1278 assert_eq!(decoded.priority(), Some(sample_priority()));
1279 assert_eq!(
1280 decoded.mapped_address(),
1281 Some("192.0.2.1:32853".parse().expect("valid"))
1282 );
1283 assert_eq!(
1284 decoded.role(),
1285 Some(RoleAttribute::Controlling {
1286 tiebreaker: SAMPLE_TIEBREAKER,
1287 nominate: true,
1288 })
1289 );
1290
1291 let controlled = Message::new(Class::Request, SAMPLE_ID)
1293 .with(Attribute::IceControlled(SAMPLE_TIEBREAKER))
1294 .encode(None)
1295 .expect("encodes");
1296 assert_eq!(
1297 Message::decode(&controlled).expect("decodes").role(),
1298 Some(RoleAttribute::Controlled {
1299 tiebreaker: SAMPLE_TIEBREAKER
1300 })
1301 );
1302 }
1303
1304 #[test]
1308 fn an_ipv6_mapped_address_round_trips() {
1309 let address: SocketAddr = "[2001:db8::1]:32853".parse().expect("valid");
1310 let value = encode_xor_mapped(address, &SAMPLE_ID);
1311 assert_eq!(value.len(), 20);
1312 assert_ne!(&value[4..20], &[0u8; 16], "the address must be obfuscated");
1313 assert_eq!(decode_xor_mapped(&value, &SAMPLE_ID), Some(address));
1314 assert_ne!(
1315 decode_xor_mapped(&value, &[0u8; 12]),
1316 Some(address),
1317 "the transaction ID is part of the key"
1318 );
1319 }
1320
1321 #[test]
1323 fn use_candidate_is_a_zero_length_flag_only_the_controlling_agent_can_send() {
1324 let peering = sample_sender();
1325 let nominating = connectivity_check(
1326 SAMPLE_ID,
1327 &peering,
1328 sample_priority(),
1329 RoleAttribute::Controlling {
1330 tiebreaker: SAMPLE_TIEBREAKER,
1331 nominate: true,
1332 },
1333 )
1334 .expect("encodes");
1335 let decoded = Message::decode(&nominating).expect("decodes");
1336 assert!(decoded.use_candidate());
1337 assert!(decoded.attributes().contains(&Attribute::UseCandidate));
1338
1339 let flag = Message::new(Class::Request, SAMPLE_ID)
1341 .with(Attribute::UseCandidate)
1342 .encode(None)
1343 .expect("encodes");
1344 assert_eq!(read_u16(&flag, HEADER_LEN), Some(ATTR_USE_CANDIDATE));
1345 assert_eq!(read_u16(&flag, HEADER_LEN + 2), Some(0), "zero length");
1346
1347 let controlled = connectivity_check(
1350 SAMPLE_ID,
1351 &peering,
1352 sample_priority(),
1353 RoleAttribute::Controlled {
1354 tiebreaker: SAMPLE_TIEBREAKER,
1355 },
1356 )
1357 .expect("encodes");
1358 assert!(
1359 !Message::decode(&controlled)
1360 .expect("decodes")
1361 .use_candidate()
1362 );
1363 }
1364
1365 #[test]
1367 fn a_role_conflict_is_a_487_error_response() {
1368 let peering = sample_receiver();
1369 let bytes = role_conflict(SAMPLE_ID, &peering).expect("encodes");
1370 let decoded = Message::decode(&bytes).expect("decodes");
1371
1372 assert_eq!(decoded.class(), Class::Error);
1373 assert_eq!(decoded.error_code(), Some(ROLE_CONFLICT));
1374 assert!(
1375 decoded.verify_integrity(peering.inbound_key()),
1376 "a response to a check that arrived is keyed with our password"
1377 );
1378 assert_eq!(&bytes[HEADER_LEN + 4..HEADER_LEN + 8], &[0, 0, 4, 87]);
1380 }
1381
1382 #[test]
1385 fn a_keepalive_is_a_binding_indication_with_a_fingerprint_and_nothing_else() {
1386 let bytes = keepalive(SAMPLE_ID).expect("encodes");
1387 assert_eq!(
1388 bytes.len(),
1389 HEADER_LEN + FINGERPRINT_ATTR_LEN,
1390 "a header and one attribute"
1391 );
1392 assert_eq!(&bytes[0..2], &[0x00, 0x11], "Binding Indication");
1393
1394 let decoded = Message::decode(&bytes).expect("decodes");
1395 assert_eq!(decoded.class(), Class::Indication);
1396 assert!(decoded.attributes().is_empty(), "§11: nothing else");
1397 assert!(decoded.has_fingerprint(), "§11 SHOULD, for demultiplexing");
1398 assert!(
1399 !decoded.has_integrity(),
1400 "§11: MUST NOT utilize any authentication mechanism"
1401 );
1402 assert!(
1403 !decoded.verify_integrity(SAMPLE_PASSWORD),
1404 "an unauthenticated message never verifies against any key"
1405 );
1406 }
1407
1408 #[test]
1411 fn a_fingerprint_that_does_not_match_is_a_dropped_datagram() {
1412 let mut bytes = hex(SAMPLE_REQUEST);
1413 let last = bytes.len() - 1;
1414 bytes[last] ^= 0x01;
1415 assert_eq!(Message::decode(&bytes), Err(Error::Fingerprint));
1416 }
1417
1418 #[test]
1421 fn an_attribute_appended_after_message_integrity_is_ignored() {
1422 let peering = sample_sender();
1423 let honest = connectivity_check(
1424 SAMPLE_ID,
1425 &peering,
1426 sample_priority(),
1427 RoleAttribute::Controlled {
1428 tiebreaker: SAMPLE_TIEBREAKER,
1429 },
1430 )
1431 .expect("encodes");
1432
1433 let split = honest.len() - FINGERPRINT_ATTR_LEN;
1436 let mut forged = honest[..split].to_vec();
1437 push_attribute(&mut forged, ATTR_USE_CANDIDATE, &[]).expect("encodes");
1438 set_length(&mut forged, FINGERPRINT_ATTR_LEN).expect("fits");
1439 let crc = crc32(&forged) ^ FINGERPRINT_XOR;
1440 push_attribute(&mut forged, ATTR_FINGERPRINT, &crc.to_be_bytes()).expect("encodes");
1441
1442 let decoded = Message::decode(&forged).expect("decodes");
1443 assert!(decoded.has_fingerprint(), "the CRC was repaired");
1444 assert!(
1445 decoded.verify_integrity(peering.outbound_key()),
1446 "the bytes the tag covers are untouched"
1447 );
1448 assert!(
1449 !decoded.use_candidate(),
1450 "an unauthenticated USE-CANDIDATE must not nominate a pair"
1451 );
1452 }
1453
1454 #[test]
1461 fn an_unknown_attribute_cannot_smuggle_in_an_integrity_value() {
1462 for kind in [ATTR_MESSAGE_INTEGRITY, ATTR_FINGERPRINT] {
1463 let forged = Message::new(Class::Request, SAMPLE_ID)
1464 .with(Attribute::Unknown {
1465 kind,
1466 value: vec![0; 20],
1467 })
1468 .with(Attribute::Username("evtj:h6vY".to_owned()));
1469 assert_eq!(
1470 forged.encode(Some(SAMPLE_PASSWORD)),
1471 Err(Error::ReservedAttribute(kind))
1472 );
1473 }
1474
1475 let passthrough = Message::new(Class::Request, SAMPLE_ID)
1477 .with(Attribute::Unknown {
1478 kind: 0x8050,
1479 value: vec![1, 2, 3],
1480 })
1481 .encode(None)
1482 .expect("encodes");
1483 assert!(Message::decode(&passthrough).is_ok());
1484 }
1485
1486 #[test]
1490 fn an_error_code_outside_rfc_5389s_range_is_refused_rather_than_folded() {
1491 for code in [0, 99, 299, 700, 800, 1000, u16::MAX] {
1492 let message = Message::new(Class::Error, SAMPLE_ID).with(Attribute::ErrorCode {
1493 code,
1494 reason: String::new(),
1495 });
1496 assert_eq!(
1497 message.encode(Some(SAMPLE_PASSWORD)),
1498 Err(Error::MalformedAttribute(ATTR_ERROR_CODE)),
1499 "{code} is not an error code §15.6 defines"
1500 );
1501 }
1502 for code in [*ERROR_CODES.start(), ROLE_CONFLICT, *ERROR_CODES.end()] {
1503 let bytes = Message::new(Class::Error, SAMPLE_ID)
1504 .with(Attribute::ErrorCode {
1505 code,
1506 reason: "because".to_owned(),
1507 })
1508 .encode(None)
1509 .expect("encodes");
1510 assert_eq!(
1511 Message::decode(&bytes).expect("decodes").error_code(),
1512 Some(code)
1513 );
1514 }
1515
1516 let mut bytes = role_conflict(SAMPLE_ID, &sample_receiver()).expect("encodes");
1518 bytes[HEADER_LEN + 6] = 7;
1519 let split = bytes.len() - FINGERPRINT_ATTR_LEN;
1520 let crc = crc32(&bytes[..split]) ^ FINGERPRINT_XOR;
1521 bytes[split + 4..].copy_from_slice(&crc.to_be_bytes());
1522 assert_eq!(
1523 Message::decode(&bytes),
1524 Err(Error::MalformedAttribute(ATTR_ERROR_CODE))
1525 );
1526 }
1527
1528 #[test]
1531 fn a_priority_outside_rfc_8839s_range_is_rejected() {
1532 let mut bytes = Message::new(Class::Request, SAMPLE_ID)
1533 .with(Attribute::Priority(Priority::MAX))
1534 .encode(None)
1535 .expect("encodes");
1536 assert!(Message::decode(&bytes).is_ok());
1537
1538 bytes[HEADER_LEN + 4..HEADER_LEN + 8].copy_from_slice(&0x8000_0000_u32.to_be_bytes());
1540 let split = bytes.len() - FINGERPRINT_ATTR_LEN;
1541 let crc = crc32(&bytes[..split]) ^ FINGERPRINT_XOR;
1542 bytes[split + 4..].copy_from_slice(&crc.to_be_bytes());
1543 assert_eq!(
1544 Message::decode(&bytes),
1545 Err(Error::MalformedAttribute(ATTR_PRIORITY))
1546 );
1547 }
1548
1549 #[test]
1553 fn no_prefix_of_a_real_message_panics() {
1554 let bytes = hex(SAMPLE_REQUEST);
1555 for length in 0..=bytes.len() {
1556 let _ = Message::decode(&bytes[..length]);
1557 }
1558 }
1559
1560 #[test]
1563 fn no_single_byte_corruption_of_a_real_message_panics() {
1564 let bytes = hex(SAMPLE_REQUEST);
1565 for index in 0..bytes.len() {
1566 for pattern in [0x00, 0x01, 0x7f, 0x80, 0xff] {
1567 let mut corrupted = bytes.clone();
1568 corrupted[index] = pattern;
1569 let _ = Message::decode(&corrupted);
1570 }
1571 }
1572 }
1573
1574 #[test]
1579 fn arbitrary_bytes_behind_a_valid_stun_header_never_panic() {
1580 let mut seed = 0x5354_554e_u64;
1581 let mut next = || {
1582 seed = seed
1583 .wrapping_mul(6_364_136_223_846_793_005)
1584 .wrapping_add(1_442_695_040_888_963_407);
1585 u8::try_from(seed >> 56).unwrap_or_default()
1586 };
1587 for _ in 0..2_000 {
1588 let body_len = usize::from(next()) * 2;
1589 let mut datagram = Vec::with_capacity(HEADER_LEN + body_len);
1590 datagram.extend_from_slice(&[0x00, 0x01]);
1591 datagram.extend_from_slice(&u16::try_from(body_len).unwrap_or_default().to_be_bytes());
1592 datagram.extend_from_slice(&MAGIC_COOKIE.to_be_bytes());
1593 datagram.extend_from_slice(&SAMPLE_ID);
1594 datagram.extend((0..body_len).map(|_| next()));
1595 let _ = Message::decode(&datagram);
1596 }
1597 }
1598
1599 #[test]
1601 fn a_length_field_past_the_end_is_an_error() {
1602 let mut bytes = hex(SAMPLE_REQUEST);
1603 bytes[2..4].copy_from_slice(&u16::MAX.to_be_bytes());
1604 assert_eq!(Message::decode(&bytes), Err(Error::Truncated));
1605
1606 let mut bytes = hex(SAMPLE_REQUEST);
1608 bytes[22..24].copy_from_slice(&u16::MAX.to_be_bytes());
1609 assert_eq!(Message::decode(&bytes), Err(Error::Truncated));
1610 }
1611
1612 #[test]
1615 fn a_datagram_that_is_not_a_binding_message_is_refused() {
1616 assert_eq!(Message::decode(&[]), Err(Error::NotStun));
1617 assert_eq!(
1618 Message::decode(b"INVITE sip:bob@example.com SIP/2.0\r\n\r\n"),
1619 Err(Error::NotStun)
1620 );
1621
1622 let mut allocate = keepalive(SAMPLE_ID).expect("encodes");
1623 allocate[0..2].copy_from_slice(&0x0003_u16.to_be_bytes());
1624 assert_eq!(Message::decode(&allocate), Err(Error::UnsupportedMethod(3)));
1625 }
1626
1627 #[test]
1630 fn what_this_module_encodes_is_stun_by_the_transport_crates_own_test() {
1631 let peering = sample_sender();
1632 for bytes in [
1633 connectivity_check(
1634 SAMPLE_ID,
1635 &peering,
1636 sample_priority(),
1637 RoleAttribute::Controlled {
1638 tiebreaker: SAMPLE_TIEBREAKER,
1639 },
1640 )
1641 .expect("encodes"),
1642 check_success(
1643 SAMPLE_ID,
1644 &peering,
1645 "192.0.2.1:32853".parse().expect("valid"),
1646 )
1647 .expect("encodes"),
1648 role_conflict(SAMPLE_ID, &peering).expect("encodes"),
1649 keepalive(SAMPLE_ID).expect("encodes"),
1650 ] {
1651 assert!(is_stun(&bytes), "{bytes:02x?}");
1652 assert_eq!(crate::dtls::classify(&bytes), crate::dtls::Arriving::Stun);
1653 }
1654 }
1655
1656 #[test]
1658 fn the_crc_matches_the_published_check_value() {
1659 assert_eq!(crc32(b"123456789"), 0xcbf4_3926);
1660 }
1661
1662 #[test]
1663 fn the_port_key_is_the_top_half_of_the_cookie() {
1664 assert_eq!(u32::from(PORT_KEY) << 16, MAGIC_COOKIE & 0xffff_0000);
1665 }
1666
1667 #[test]
1669 fn the_message_type_round_trips_through_the_class_bits() {
1670 for (class, raw) in [
1671 (Class::Request, 0x0001),
1672 (Class::Indication, 0x0011),
1673 (Class::Success, 0x0101),
1674 (Class::Error, 0x0111),
1675 ] {
1676 assert_eq!(message_type(class, METHOD_BINDING), raw);
1677 assert_eq!(split_type(raw), (class, METHOD_BINDING));
1678 }
1679 }
1680
1681 #[test]
1683 fn a_transaction_id_is_fresh_each_time() {
1684 assert_ne!(new_transaction_id(), new_transaction_id());
1685 }
1686}