mumble_protocol_2x/
ping.rs

1//! Ping messages and codec
2//!
3//! A Mumble client can send periodic UDP [PingPacket]s to servers
4//! in order to query their current state and measure latency.
5//! A server will usually respond with a corresponding [PongPacket] containing
6//! the requested details.
7//!
8//! Both packets are of fixed size and can be converted to/from `u8` arrays/slices via
9//! the respective `From`/`TryFrom` impls.
10
11/// A ping packet sent to the server.
12#[derive(Clone, Debug, PartialEq)]
13pub struct PingPacket {
14    /// Opaque, client-generated id.
15    ///
16    /// Will be returned by the server unmodified and can be used to correlate
17    /// pong replies to ping requests to e.g. calculate latency.
18    pub id: u64,
19}
20
21/// A pong packet sent to the client in reply to a previously received [PingPacket].
22#[derive(Clone, Debug, PartialEq)]
23pub struct PongPacket {
24    /// Opaque, client-generated id.
25    ///
26    /// Should match the value in the corresponding [PingPacket].
27    pub id: u64,
28
29    /// Server version. E.g. `0x010300` for `1.3.0`.
30    pub version: u32,
31
32    /// Current amount of users connected to the server.
33    pub users: u32,
34
35    /// Configured limit on the amount of users which can be connected to the server.
36    pub max_users: u32,
37
38    /// Maximum bandwidth for server-bound speech per client in bits per second
39    pub bandwidth: u32,
40}
41
42/// Error during parsing of a [PingPacket].
43#[derive(Clone, Debug, PartialEq)]
44pub enum ParsePingError {
45    /// Ping packets must always be 12 bytes in size.
46    InvalidSize,
47    /// Ping packets must have an all zero header of 4 bytes.
48    InvalidHeader,
49}
50
51impl TryFrom<&[u8]> for PingPacket {
52    type Error = ParsePingError;
53    fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
54        match <[u8; 12]>::try_from(buf) {
55            Ok(array) => {
56                if array[0..4] != [0, 0, 0, 0] {
57                    Err(ParsePingError::InvalidHeader)
58                } else {
59                    Ok(Self {
60                        id: u64::from_be_bytes(array[4..12].try_into().unwrap()),
61                    })
62                }
63            }
64            Err(_) => Err(ParsePingError::InvalidSize),
65        }
66    }
67}
68
69impl From<PingPacket> for [u8; 12] {
70    fn from(packet: PingPacket) -> Self {
71        let id = packet.id.to_be_bytes();
72        // Is there no nicer way to do this?
73        [
74            0, 0, 0, 0, id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7],
75        ]
76    }
77}
78
79/// Error during parsing of a [PongPacket].
80#[derive(Clone, Debug, PartialEq)]
81pub enum ParsePongError {
82    /// Pong packets must always be 24 bytes in size.
83    InvalidSize,
84}
85
86impl TryFrom<&[u8]> for PongPacket {
87    type Error = ParsePongError;
88    fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
89        match <[u8; 24]>::try_from(buf) {
90            Ok(array) => Ok(Self {
91                version: u32::from_be_bytes(array[0..4].try_into().unwrap()),
92                id: u64::from_be_bytes(array[4..12].try_into().unwrap()),
93                users: u32::from_be_bytes(array[12..16].try_into().unwrap()),
94                max_users: u32::from_be_bytes(array[16..20].try_into().unwrap()),
95                bandwidth: u32::from_be_bytes(array[20..24].try_into().unwrap()),
96            }),
97            Err(_) => Err(ParsePongError::InvalidSize),
98        }
99    }
100}
101
102impl From<PongPacket> for [u8; 24] {
103    fn from(packet: PongPacket) -> Self {
104        let version = packet.version.to_be_bytes();
105        let id = packet.id.to_be_bytes();
106        let users = packet.users.to_be_bytes();
107        let max_users = packet.max_users.to_be_bytes();
108        let bandwidth = packet.bandwidth.to_be_bytes();
109        // Is there no nicer way to do this?
110        [
111            version[0],
112            version[1],
113            version[2],
114            version[3],
115            id[0],
116            id[1],
117            id[2],
118            id[3],
119            id[4],
120            id[5],
121            id[6],
122            id[7],
123            users[0],
124            users[1],
125            users[2],
126            users[3],
127            max_users[0],
128            max_users[1],
129            max_users[2],
130            max_users[3],
131            bandwidth[0],
132            bandwidth[1],
133            bandwidth[2],
134            bandwidth[3],
135        ]
136    }
137}