1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! 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.

use std::convert::TryFrom;
use std::convert::TryInto;

/// 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<Self, Self::Error> {
        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<PingPacket> 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<Self, Self::Error> {
        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<PongPacket> 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],
        ]
    }
}