Skip to main content

flowscope/stun/
types.rs

1//! Public types for the STUN parser.
2
3use std::net::SocketAddr;
4
5/// One parsed STUN datagram.
6#[derive(Debug, Clone, PartialEq, Eq)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8#[non_exhaustive]
9pub struct StunMessage {
10    /// Message class (request / indication / success / error).
11    pub class: StunClass,
12    /// 12-bit method (Binding = 1, TURN allocations etc. as
13    /// Other(u16) when not the standard Binding).
14    pub method: u16,
15    /// 96-bit transaction identifier (12 raw bytes).
16    pub transaction_id: [u8; 12],
17    /// Decoded MAPPED-ADDRESS / XOR-MAPPED-ADDRESS. The
18    /// XOR variant is normalized back to the cleartext
19    /// address.
20    pub mapped_address: Option<SocketAddr>,
21    /// USERNAME attribute value (UTF-8 lossy decoded).
22    pub username: Option<String>,
23    /// SOFTWARE attribute — vendor banner.
24    pub software: Option<String>,
25}
26
27/// RFC 5389 §6 message class.
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
31#[non_exhaustive]
32pub enum StunClass {
33    Request,
34    Indication,
35    SuccessResponse,
36    ErrorResponse,
37}
38
39impl StunClass {
40    /// Decode the 2-bit class field embedded in the message
41    /// type per RFC 5389 §6 (bits C0 and C1).
42    pub fn from_type_bits(type_word: u16) -> Self {
43        // Class bits are at positions 4 (C0) and 8 (C1).
44        let c0 = (type_word >> 4) & 0x01;
45        let c1 = (type_word >> 8) & 0x01;
46        match (c1, c0) {
47            (0, 0) => Self::Request,
48            (0, 1) => Self::Indication,
49            (1, 0) => Self::SuccessResponse,
50            (1, 1) => Self::ErrorResponse,
51            _ => unreachable!(),
52        }
53    }
54
55    /// Stable slug for metric labels / logs.
56    pub fn as_str(&self) -> &'static str {
57        match self {
58            Self::Request => "request",
59            Self::Indication => "indication",
60            Self::SuccessResponse => "success_response",
61            Self::ErrorResponse => "error_response",
62        }
63    }
64}
65
66/// Extract the 12-bit method per RFC 5389 §6.
67pub(crate) fn extract_method(type_word: u16) -> u16 {
68    // Method bits are at positions 0..4, 5..8, 9..14 (12 bits total,
69    // interleaved with the class bits).
70    let m0 = type_word & 0x000f;
71    let m4 = (type_word >> 5) & 0x0007; // 3 bits
72    let m7 = (type_word >> 9) & 0x001f; // 5 bits
73    (m7 << 7) | (m4 << 4) | m0
74}