stun_coder/message/decode.rs
1use std::io::Cursor;
2
3use crate::definitions::{STUN_FINGERPRINT_ATTR_SIZE, STUN_INTEGRITY_ATTR_SIZE};
4use crate::StunHeader;
5use crate::{AttributeDecodeError, StunAttribute};
6
7pub use super::errors::{IntegrityKeyGenerationError, MessageDecodeError, MessageEncodeError};
8use super::message::StunMessage;
9
10impl StunMessage {
11 /// Decodes and returns the STUN message
12 ///
13 /// Arguments:
14 ///
15 /// * `bytes`: binary encoded message to decode from
16 /// * `integrity_password`: Optionally set key that will be used for message integrity verification
17 pub fn decode(
18 bytes: &[u8],
19 integrity_password: Option<&str>,
20 ) -> Result<Self, MessageDecodeError> {
21 let mut cursor = Cursor::new(bytes);
22
23 // Decode header
24 let header = StunHeader::decode(&mut cursor)?;
25 // Decode attributes
26 let mut attributes = Vec::new();
27
28 let mut integrity_attr_passed = false;
29
30 // Track for username/realm occurrences
31 let mut username = None;
32 let mut realm = None;
33
34 // Message length does not include the 20-byte header.
35 while cursor.position() < 20 + header.message_len as u64 {
36 let decoded = StunAttribute::decode(&mut cursor, header.transaction_id);
37
38 match decoded {
39 Ok(decoded) => {
40 // Ignore all attributes after the MESSAGE-INTEGRITY attribute.
41 // As per [RFC5389 Section 15.4](https://tools.ietf.org/html/rfc5389#section-15.4)
42 if !integrity_attr_passed {
43 attributes.push(decoded.clone());
44 }
45
46 // Handle Fingerprint and MessageIntegrity attributes
47 match decoded {
48 StunAttribute::Username { value } => {
49 username = Some(value);
50 }
51 StunAttribute::Realm { value } => {
52 realm = Some(value);
53 }
54 StunAttribute::Fingerprint { value } => {
55 let attr_pos = cursor.position() as usize - STUN_FINGERPRINT_ATTR_SIZE;
56
57 // Make sure the Fingerprint attribute is the last one
58 if cursor.position() != bytes.len() as u64 {
59 return Err(
60 MessageDecodeError::IncorrectFingerprintAttributePosition {
61 msg_len: bytes.len(),
62 attr_pos: attr_pos as usize,
63 },
64 );
65 }
66
67 // Compute fingerprint for verification
68 let computed_fingerprint =
69 Self::calculate_fingerprint(&cursor.get_ref()[0..attr_pos]);
70
71 // Make sure the fingerprint matches
72 if computed_fingerprint != value {
73 return Err(MessageDecodeError::FingerprintMismatch {
74 attr_value: value,
75 computed_value: computed_fingerprint,
76 });
77 }
78
79 if integrity_attr_passed {
80 // Push the attribute to the list explicitly since it's after the MessageIntegrity attribute
81 attributes.push(decoded);
82 }
83 }
84 StunAttribute::MessageIntegrity { key } => {
85 // Mark MessageIntegrity attribute as passed so we can ignore attributes that happen after it
86 // With the exception of the Fingerprint attribute
87 integrity_attr_passed = true;
88
89 // If an `integrity_password` has been supplied, recalculate and verify the HMAC value
90 if let Some(integrity_password) = integrity_password {
91 let integrity_key = Self::calculate_integrity_key(
92 integrity_password,
93 realm.clone(),
94 username.clone(),
95 )?;
96
97 let hmac = Self::calculate_integrity_hash(
98 &integrity_key,
99 &cursor.get_ref()[0..(cursor.position() as usize
100 - STUN_INTEGRITY_ATTR_SIZE)
101 as usize],
102 );
103
104 // Verify message integrity
105 if hmac != key {
106 return Err(MessageDecodeError::MessageIntegrityFail {
107 attr_value: key,
108 computed_value: hmac,
109 });
110 }
111 }
112 }
113 _ => {}
114 };
115 }
116 Err(err) => {
117 match err {
118 AttributeDecodeError::UnrecognizedAttributeType { attr_type } => {
119 // Attributes with type values between 0x8000 and 0xFFFF are
120 // comprehension-optional attributes, which means that those attributes
121 // can be ignored by the STUN agent if it does not understand them.
122 // Only return an error when the attribute is comprehension-required
123 if attr_type <= 0x8000 {
124 return Err(MessageDecodeError::AttributeDecodeFailure{source: err, transaction_id: header.transaction_id});
125 }
126 }
127 // Return an error on any other attribute decoding error
128 _ => return Err(MessageDecodeError::AttributeDecodeFailure{source: err, transaction_id: header.transaction_id}),
129 }
130 }
131 }
132 }
133
134 Ok(Self { header, attributes })
135 }
136}