stun_coder/header/
mod.rs

1use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt};
2use num_traits::FromPrimitive;
3use std::io::{Cursor, Read, Write};
4
5mod errors;
6mod message_class;
7mod message_method;
8
9use crate::definitions::{StunTransactionId, STUN_MAGIC_COOKIE_U32};
10use crate::utils::generate_transaction_id;
11pub use errors::{HeaderDecodeError, HeaderEncodeError};
12pub use message_class::StunMessageClass;
13pub use message_method::StunMessageMethod;
14
15#[derive(Debug, Copy, Clone)]
16/// [STUN message header](https://tools.ietf.org/html/rfc5389#section-6)
17///
18/// All STUN messages MUST start with a 20-byte header followed by zero
19/// or more Attributes.  The STUN header contains a STUN message type,
20/// magic cookie, transaction ID, and message length.
21///
22/// The most significant 2 bits of every STUN message MUST be zeroes.
23/// This can be used to differentiate STUN packets from other protocols
24/// when STUN is multiplexed with other protocols on the same port.
25///
26/// The message type defines the message class (request, success
27/// response, failure response, or indication) and the message method
28/// (the primary function) of the STUN message.  Although there are four
29/// message classes, there are only two types of transactions in STUN:
30/// request/response transactions (which consist of a request message and
31/// a response message) and indication transactions (which consist of a
32/// single indication message).  Response classes are split into error
33/// and success responses to aid in quickly processing the STUN message.
34
35pub struct StunHeader {
36    /// STUN message class
37    pub message_class: StunMessageClass,
38    /// STUN message method
39    pub message_method: StunMessageMethod,
40    /// STUN transaction id
41    pub transaction_id: StunTransactionId,
42    /// STUN message length
43    /// Only set to a non-zero value when decoding the header
44    pub message_len: u16,
45}
46
47impl StunHeader {
48    /// Creates a new header
49    ///
50    /// If no `transaction_id` is provided, one is randomly generated and set
51    /// The `message_len` is set as zero and left untouched unless a decoder sets it.
52    pub(crate) fn new(
53        message_method: StunMessageMethod,
54        message_class: StunMessageClass,
55        transaction_id: Option<StunTransactionId>,
56    ) -> Self {
57        // Pick a transaction_id
58        let transaction_id = match transaction_id {
59            Some(id) => id,
60            None => generate_transaction_id(),
61        };
62
63        Self {
64            message_method,
65            message_class,
66            transaction_id,
67            message_len: 0, // Placeholder for the encoder to later fill in
68        }
69    }
70
71    /// Decodes and returns a STUN message header
72    pub(crate) fn decode(cursor: &mut Cursor<&[u8]>) -> Result<Self, HeaderDecodeError> {
73        let stun_type_field = cursor.read_u16::<NetworkEndian>()?;
74        let msg_len = cursor.read_u16::<NetworkEndian>()?;
75        let magic_cookie = cursor.read_u32::<NetworkEndian>()?;
76
77        if magic_cookie != STUN_MAGIC_COOKIE_U32 {
78            return Err(HeaderDecodeError::MagicCookieMismatch());
79        }
80
81        let mut transaction_id = [0; 12];
82        cursor.read_exact(&mut transaction_id)?;
83
84        let stun_class = stun_type_field & 0b0000_0001_0001_0000;
85        let stun_method = stun_type_field & 0b1111_1110_1110_1111;
86
87        let message_method: StunMessageMethod = FromPrimitive::from_u16(stun_method)
88            .ok_or(HeaderDecodeError::UnrecognizedMessageMethod(stun_method))?;
89        let message_class: StunMessageClass = FromPrimitive::from_u16(stun_class)
90            .ok_or(HeaderDecodeError::UnrecognizedMessageClass(stun_class))?;
91
92        Ok(Self {
93            message_method,
94            message_class,
95            message_len: msg_len,
96            transaction_id,
97        })
98    }
99
100    /// Encodes itself into the binary representation defined by [RFC5389](https://tools.ietf.org/html/rfc5389)
101    pub(crate) fn encode(&self) -> Result<Vec<u8>, HeaderEncodeError> {
102        let bytes = Vec::new();
103        let mut cursor = Cursor::new(bytes);
104
105        let stun_type_field = self.message_class as u16 | self.message_method as u16;
106
107        cursor.write_u16::<NetworkEndian>(stun_type_field)?;
108        cursor.write_u16::<NetworkEndian>(self.message_len)?;
109        cursor.write_u32::<NetworkEndian>(STUN_MAGIC_COOKIE_U32)?;
110        cursor.write_all(&self.transaction_id)?;
111
112        Ok(cursor.get_ref().to_vec())
113    }
114}