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}