// This code was taken from mumble-protocol-2x (https://github.com/dblsaiko/rust-mumble-protocol) // and originally from mumble-protocol (https://github.com/Johni0702/rust-mumble-protocol) // These projects are licensed under MIT and Apache 2.0. //! Ping messages and codec //! //! A Mumble client can send periodic UDP [PingPacket]s to servers //! in order to query their current state and measure latency. //! A server will usually respond with a corresponding [PongPacket] containing //! the requested details. //! //! Both packets are of fixed size and can be converted to/from `u8` arrays/slices via //! the respective `From`/`TryFrom` impls. /// A ping packet sent to the server. #[derive(Clone, Debug, PartialEq)] pub struct PingPacket { /// Opaque, client-generated id. /// /// Will be returned by the server unmodified and can be used to correlate /// pong replies to ping requests to e.g. calculate latency. pub id: u64, } /// A pong packet sent to the client in reply to a previously received [PingPacket]. #[derive(Clone, Debug, PartialEq)] pub struct PongPacket { /// Opaque, client-generated id. /// /// Should match the value in the corresponding [PingPacket]. pub id: u64, /// Server version. E.g. `0x010300` for `1.3.0`. pub version: u32, /// Current amount of users connected to the server. pub users: u32, /// Configured limit on the amount of users which can be connected to the server. pub max_users: u32, /// Maximum bandwidth for server-bound speech per client in bits per second pub bandwidth: u32, } /// Error during parsing of a [PingPacket]. #[derive(Clone, Debug, PartialEq)] pub enum ParsePingError { /// Ping packets must always be 12 bytes in size. InvalidSize, /// Ping packets must have an all zero header of 4 bytes. InvalidHeader, } impl TryFrom<&[u8]> for PingPacket { type Error = ParsePingError; fn try_from(buf: &[u8]) -> Result { match <[u8; 12]>::try_from(buf) { Ok(array) => { if array[0..4] != [0, 0, 0, 0] { Err(ParsePingError::InvalidHeader) } else { Ok(Self { id: u64::from_be_bytes(array[4..12].try_into().unwrap()), }) } } Err(_) => Err(ParsePingError::InvalidSize), } } } impl From for [u8; 12] { fn from(packet: PingPacket) -> Self { let id = packet.id.to_be_bytes(); // Is there no nicer way to do this? [ 0, 0, 0, 0, id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], ] } } /// Error during parsing of a [PongPacket]. #[derive(Clone, Debug, PartialEq)] pub enum ParsePongError { /// Pong packets must always be 24 bytes in size. InvalidSize, } impl TryFrom<&[u8]> for PongPacket { type Error = ParsePongError; fn try_from(buf: &[u8]) -> Result { match <[u8; 24]>::try_from(buf) { Ok(array) => Ok(Self { version: u32::from_be_bytes(array[0..4].try_into().unwrap()), id: u64::from_be_bytes(array[4..12].try_into().unwrap()), users: u32::from_be_bytes(array[12..16].try_into().unwrap()), max_users: u32::from_be_bytes(array[16..20].try_into().unwrap()), bandwidth: u32::from_be_bytes(array[20..24].try_into().unwrap()), }), Err(_) => Err(ParsePongError::InvalidSize), } } } impl From for [u8; 24] { fn from(packet: PongPacket) -> Self { let version = packet.version.to_be_bytes(); let id = packet.id.to_be_bytes(); let users = packet.users.to_be_bytes(); let max_users = packet.max_users.to_be_bytes(); let bandwidth = packet.bandwidth.to_be_bytes(); // Is there no nicer way to do this? [ version[0], version[1], version[2], version[3], id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], users[0], users[1], users[2], users[3], max_users[0], max_users[1], max_users[2], max_users[3], bandwidth[0], bandwidth[1], bandwidth[2], bandwidth[3], ] } }