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