mumble_protocol_2x/
control.rs

1//! Control channel messages and codecs
2
3use std::io;
4use std::io::Cursor;
5use std::marker::PhantomData;
6
7use bytes::Buf;
8use bytes::BufMut;
9use bytes::Bytes;
10use bytes::BytesMut;
11use protobuf::Error as ProtobufError;
12use protobuf::Message;
13
14use crate::voice::Clientbound;
15use crate::voice::Serverbound;
16use crate::voice::VoiceCodec;
17use crate::voice::VoicePacket;
18use crate::voice::VoicePacketDst;
19
20/// ProtoBuf message types for all Mumble messages.
21#[allow(renamed_and_removed_lints)] // protobuf is missing `clippy::` prefix
22#[allow(missing_docs)] // these would have to be auto-generated by protobuf
23pub mod msgs {
24    /// Mumble message type to packet ID mappings.
25    pub mod id {
26        pub use super::super::generated_id::*;
27    }
28
29    include!(concat!(env!("OUT_DIR"), "/proto/mod.rs"));
30}
31
32/// Raw/not-yet-parsed Mumble control packet.
33#[derive(Clone, Debug, PartialEq)]
34pub struct RawControlPacket {
35    /// Packet ID
36    ///
37    /// See [msgs::id].
38    pub id: u16,
39    /// Raw message bytes.
40    pub bytes: Bytes,
41}
42
43/// A `Codec` implementation that parses a stream of data into [RawControlPacket]s.
44#[derive(Debug)]
45pub struct RawControlCodec;
46
47impl RawControlCodec {
48    /// Creates a new RawControlCodec.
49    pub fn new() -> Self {
50        Default::default()
51    }
52}
53
54impl Default for RawControlCodec {
55    fn default() -> Self {
56        RawControlCodec
57    }
58}
59
60impl RawControlCodec {
61    fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<RawControlPacket>, io::Error> {
62        let buf_len = buf.len();
63        if buf_len >= 6 {
64            let mut buf = Cursor::new(buf);
65            let id = buf.get_u16();
66            let len = buf.get_u32() as usize;
67            if len > 0x7f_ffff {
68                Err(io::Error::new(io::ErrorKind::Other, "packet too long"))
69            } else if buf_len >= 6 + len {
70                let mut bytes = buf.into_inner().split_to(6 + len);
71                bytes.advance(6);
72                let bytes = bytes.freeze();
73                Ok(Some(RawControlPacket { id, bytes }))
74            } else {
75                Ok(None)
76            }
77        } else {
78            Ok(None)
79        }
80    }
81}
82
83#[cfg(feature = "tokio-codec")]
84impl tokio_util::codec::Decoder for RawControlCodec {
85    type Item = RawControlPacket;
86    type Error = io::Error;
87
88    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
89        self.decode(src)
90    }
91}
92
93#[cfg(feature = "asynchronous-codec")]
94impl asynchronous_codec::Decoder for RawControlCodec {
95    type Item = RawControlPacket;
96    type Error = io::Error;
97
98    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
99        self.decode(src)
100    }
101}
102
103impl RawControlCodec {
104    fn encode(&mut self, item: RawControlPacket, dst: &mut BytesMut) -> Result<(), io::Error> {
105        let id = item.id;
106        let bytes = &item.bytes;
107        let len = bytes.len();
108        dst.reserve(6 + len);
109        dst.put_u16(id);
110        dst.put_u32(len as u32);
111        dst.put_slice(bytes);
112        Ok(())
113    }
114}
115
116#[cfg(feature = "tokio-codec")]
117impl tokio_util::codec::Encoder<RawControlPacket> for RawControlCodec {
118    type Error = io::Error;
119
120    fn encode(&mut self, item: RawControlPacket, dst: &mut BytesMut) -> Result<(), io::Error> {
121        self.encode(item, dst)
122    }
123}
124
125#[cfg(feature = "asynchronous-codec")]
126impl asynchronous_codec::Encoder for RawControlCodec {
127    type Item = RawControlPacket;
128    type Error = io::Error;
129
130    fn encode(&mut self, item: RawControlPacket, dst: &mut BytesMut) -> Result<(), io::Error> {
131        self.encode(item, dst)
132    }
133}
134
135/// A `Codec` implementation that parses a stream of data into [ControlPacket]s.
136///
137/// Since [VoicePacket]s can be tunneled over the control channel and their encoding and decoding
138/// depends on their destination, the control codec also needs to know the side it's on.
139/// See [ServerControlCodec] and [ClientControlCodec] for the two most reasonable configurations.
140#[derive(Debug)]
141pub struct ControlCodec<EncodeDst: VoicePacketDst, DecodeDst: VoicePacketDst> {
142    inner: RawControlCodec,
143    _encode_dst: PhantomData<EncodeDst>,
144    _decode_dst: PhantomData<DecodeDst>,
145}
146/// The [ControlCodec] used on the server side.
147pub type ServerControlCodec = ControlCodec<Clientbound, Serverbound>;
148/// The [ControlCodec] used on the client side.
149pub type ClientControlCodec = ControlCodec<Serverbound, Clientbound>;
150
151impl<EncodeDst: VoicePacketDst, DecodeDst: VoicePacketDst> ControlCodec<EncodeDst, DecodeDst> {
152    /// Creates a new control codec.
153    pub fn new() -> Self {
154        Default::default()
155    }
156}
157
158impl<EncodeDst: VoicePacketDst, DecodeDst: VoicePacketDst> Default
159    for ControlCodec<EncodeDst, DecodeDst>
160{
161    fn default() -> Self {
162        ControlCodec {
163            inner: RawControlCodec::default(),
164            _encode_dst: PhantomData,
165            _decode_dst: PhantomData,
166        }
167    }
168}
169
170impl<EncodeDst: VoicePacketDst, DecodeDst: VoicePacketDst> ControlCodec<EncodeDst, DecodeDst> {
171    fn decode(
172        &mut self,
173        src: &mut BytesMut,
174    ) -> Result<Option<ControlPacket<DecodeDst>>, io::Error> {
175        Ok(if let Some(raw_packet) = self.inner.decode(src)? {
176            Some(raw_packet.try_into()?)
177        } else {
178            None
179        })
180    }
181}
182
183#[cfg(feature = "tokio-codec")]
184impl<EncodeDst: VoicePacketDst, DecodeDst: VoicePacketDst> tokio_util::codec::Decoder
185    for ControlCodec<EncodeDst, DecodeDst>
186{
187    type Item = ControlPacket<DecodeDst>;
188    type Error = io::Error;
189
190    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
191        self.decode(src)
192    }
193}
194
195#[cfg(feature = "asynchronous-codec")]
196impl<EncodeDst: VoicePacketDst, DecodeDst: VoicePacketDst> asynchronous_codec::Decoder
197    for ControlCodec<EncodeDst, DecodeDst>
198{
199    type Item = ControlPacket<DecodeDst>;
200    type Error = io::Error;
201
202    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
203        self.decode(src)
204    }
205}
206
207#[cfg(feature = "tokio-codec")]
208impl<EncodeDst: VoicePacketDst, DecodeDst: VoicePacketDst>
209    tokio_util::codec::Encoder<ControlPacket<EncodeDst>> for ControlCodec<EncodeDst, DecodeDst>
210{
211    type Error = io::Error;
212
213    fn encode(
214        &mut self,
215        item: ControlPacket<EncodeDst>,
216        dst: &mut BytesMut,
217    ) -> Result<(), Self::Error> {
218        self.inner.encode(item.into(), dst)
219    }
220}
221
222#[cfg(feature = "asynchronous-codec")]
223impl<EncodeDst: VoicePacketDst, DecodeDst: VoicePacketDst> asynchronous_codec::Encoder
224    for ControlCodec<EncodeDst, DecodeDst>
225{
226    type Item = ControlPacket<EncodeDst>;
227    type Error = io::Error;
228
229    fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
230        self.inner.encode(item.into(), dst)
231    }
232}
233
234/// Generates packet to ID mappings which will end up in [msgs::ids].
235macro_rules! define_packet_mappings {
236    ( @def $id:expr, $name:ident) => {
237        #[allow(dead_code)]
238        #[allow(non_upper_case_globals)]
239        pub const $name: u16 = $id;
240    };
241    ( @rec $id:expr, $(#[$attr:meta])* $head:ident ) => {
242        $(#[$attr])*
243        define_packet_mappings!(@def $id, $head);
244    };
245    ( @rec $id:expr, $(#[$attr:meta])* $head:ident, $( $(#[$attr_tail:meta])* $tail:ident ),* ) => {
246        $(#[$attr])*
247        define_packet_mappings!(@def $id, $head);
248        define_packet_mappings!(@rec $id + 1, $($(#[$attr_tail])* $tail),*);
249    };
250    ( $( $(#[$attrs:meta])* $names:ident ),* ) => {
251        define_packet_mappings!(@rec 0, $($(#[$attrs])* $names),*);
252    };
253}
254
255/// Generates From impls for converting between RawCtrlPck <=> ProtoMsg => CtrlPck
256macro_rules! define_packet_from {
257    ( $Dst:ident UDPTunnel($type:ty) ) => {
258        impl<$Dst: VoicePacketDst> From<VoicePacket<Dst>> for RawControlPacket {
259            fn from(msg: VoicePacket<Dst>) -> Self {
260                let mut buf = BytesMut::new();
261
262                cfg_if::cfg_if! {
263                    if #[cfg(feature = "asynchronous-codec")] {
264                        use asynchronous_codec::Encoder as _;
265                    } else {
266                        use tokio_util::codec::Encoder as _;
267                    }
268                }
269
270                VoiceCodec::<Dst, Dst>::default()
271                    .encode(msg, &mut buf)
272                    .expect("VoiceEncoder is infallible");
273
274                Self {
275                    id: msgs::id::UDPTunnel,
276                    bytes: buf.freeze(),
277                }
278            }
279        }
280        impl<$Dst: VoicePacketDst> TryFrom<RawControlPacket> for VoicePacket<$Dst> {
281            type Error = io::Error;
282
283            fn try_from(packet: RawControlPacket) -> Result<Self, Self::Error> {
284                if packet.id == msgs::id::UDPTunnel {
285                    packet.bytes.try_into()
286                } else {
287                    Err(io::Error::new(
288                        io::ErrorKind::Other,
289                        concat!("expected packet of type ", stringify!(UDPTunnel)),
290                    ))
291                }
292            }
293        }
294        impl<$Dst: VoicePacketDst> TryFrom<Bytes> for VoicePacket<$Dst> {
295            type Error = io::Error;
296
297            fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
298                cfg_if::cfg_if! {
299                    if #[cfg(feature = "asynchronous-codec")] {
300                        use asynchronous_codec::Decoder as _;
301                    } else {
302                        use tokio_util::codec::Decoder as _;
303                    }
304                }
305
306                VoiceCodec::<$Dst, $Dst>::default()
307                    .decode(&mut BytesMut::from(bytes.as_ref()))
308                    .map(|it| it.expect("VoiceCodec is stateless"))
309            }
310        }
311        impl<$Dst: VoicePacketDst> From<$type> for ControlPacket<$Dst> {
312            fn from(inner: $type) -> Self {
313                ControlPacket::UDPTunnel(Box::new(inner))
314            }
315        }
316    };
317    ( $Dst:ident $name:ident($type:ty) ) => {
318        impl<$Dst: VoicePacketDst> From<$type> for ControlPacket<$Dst> {
319            fn from(inner: $type) -> Self {
320                ControlPacket::$name(Box::new(inner))
321            }
322        }
323        impl From<$type> for RawControlPacket {
324            fn from(msg: $type) -> Self {
325                Self {
326                    id: self::msgs::id::$name,
327                    bytes: msg.write_to_bytes().unwrap().into(),
328                }
329            }
330        }
331        impl TryFrom<RawControlPacket> for $type {
332            type Error = ProtobufError;
333
334            fn try_from(packet: RawControlPacket) -> Result<Self, Self::Error> {
335                if packet.id == msgs::id::$name {
336                    Self::try_from(packet.bytes)
337                } else {
338                    Err(io::Error::new(
339                        io::ErrorKind::Other,
340                        concat!("expected packet of type ", stringify!($name)),
341                    ).into())
342                }
343            }
344        }
345        impl TryFrom<&[u8]> for $type {
346            type Error = ProtobufError;
347
348            fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
349                Message::parse_from_bytes(bytes)
350            }
351        }
352        impl TryFrom<Bytes> for $type {
353            type Error = ProtobufError;
354
355            fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
356                bytes.as_ref().try_into()
357            }
358        }
359    };
360}
361
362/// Generates the ControlPacket enum, From impls for RawCtrlPck <=> CtrlPck and CtrlPck::name()
363macro_rules! define_packet_enum {
364    ( $Dst:ident $( $(#[$attr:meta])* $name:ident($type:ty) ),* ) => {
365        /// A parsed Mumble control packet.
366        #[derive(Debug, Clone, PartialEq)]
367        #[allow(clippy::large_enum_variant)]
368        #[non_exhaustive]
369        pub enum ControlPacket<$Dst: VoicePacketDst> {
370            $(
371                #[allow(missing_docs)]
372                $(#[$attr])*
373                $name(Box<$type>),
374            )*
375            /// A packet of unknown type.
376            Other(RawControlPacket),
377        }
378        impl<Dst: VoicePacketDst> TryFrom<RawControlPacket> for ControlPacket<$Dst> {
379            type Error = ProtobufError;
380
381            fn try_from(packet: RawControlPacket) -> Result<Self, Self::Error> {
382                Ok(match packet.id {
383                    $(
384                        $(#[$attr])*
385                        msgs::id::$name => {
386                            ControlPacket::$name(Box::new(packet.bytes.try_into()?))
387                        }
388                    )*
389                        _ => ControlPacket::Other(packet),
390                })
391            }
392        }
393        impl<Dst: VoicePacketDst> From<ControlPacket<$Dst>> for RawControlPacket {
394            fn from(packet: ControlPacket<$Dst>) -> Self {
395                match packet {
396                    $(
397                        $(#[$attr])*
398                        ControlPacket::$name(inner) => (*inner).into(),
399                    )*
400                        ControlPacket::Other(inner) => inner,
401                }
402            }
403        }
404        impl<Dst: VoicePacketDst> ControlPacket<$Dst> {
405            /// Returns the internal name of a packet (for debugging purposes).
406            pub fn name(&self) -> &'static str {
407                match self {
408                    $(
409                        $(#[$attr])*
410                        ControlPacket::$name(_) => stringify!($name),
411                    )*
412                    ControlPacket::Other(_) => "unknown",
413                }
414            }
415        }
416    };
417}
418
419macro_rules! define_packets {
420    ( < $Dst:ident > $( $(#[$attr:meta])* $name:ident($type:ty), )* ) => {
421        #[allow(missing_docs)]
422        mod generated_id {
423            define_packet_mappings!($($(#[$attr])* $name),*);
424        }
425        define_packet_enum!($Dst $($(#[$attr])* $name($type)),*);
426        $(
427            $(#[$attr])*
428            define_packet_from!($Dst $name($type));
429        )*
430    };
431}
432
433define_packets![
434    <Dst>
435    Version(msgs::Version),
436    UDPTunnel(VoicePacket<Dst>),
437    Authenticate(msgs::Authenticate),
438    Ping(msgs::Ping),
439    Reject(msgs::Reject),
440    ServerSync(msgs::ServerSync),
441    ChannelRemove(msgs::ChannelRemove),
442    ChannelState(msgs::ChannelState),
443    UserRemove(msgs::UserRemove),
444    UserState(msgs::UserState),
445    BanList(msgs::BanList),
446    TextMessage(msgs::TextMessage),
447    PermissionDenied(msgs::PermissionDenied),
448    ACL(msgs::ACL),
449    QueryUsers(msgs::QueryUsers),
450    CryptSetup(msgs::CryptSetup),
451    ContextActionModify(msgs::ContextActionModify),
452    ContextAction(msgs::ContextAction),
453    UserList(msgs::UserList),
454    VoiceTarget(msgs::VoiceTarget),
455    PermissionQuery(msgs::PermissionQuery),
456    CodecVersion(msgs::CodecVersion),
457    UserStats(msgs::UserStats),
458    RequestBlob(msgs::RequestBlob),
459    ServerConfig(msgs::ServerConfig),
460    SuggestConfig(msgs::SuggestConfig),
461    #[cfg(feature = "webrtc-extensions")]
462    WebRTC(msgs::WebRTC),
463    #[cfg(feature = "webrtc-extensions")]
464    IceCandidate(msgs::IceCandidate),
465    #[cfg(feature = "webrtc-extensions")]
466    TalkingState(msgs::TalkingState),
467];