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