1use prost::Message;
14use thiserror::Error;
15
16use crate::framing::{Frame, FramingError, TcpMessageType, write_frame};
17use crate::messages::tcp;
18
19#[derive(Debug, Clone, PartialEq)]
21pub enum ControlMessage {
22 Version(tcp::Version),
23 UdpTunnel(Vec<u8>),
26 Authenticate(tcp::Authenticate),
27 Ping(tcp::Ping),
28 Reject(tcp::Reject),
29 ServerSync(tcp::ServerSync),
30 ChannelRemove(tcp::ChannelRemove),
31 ChannelState(tcp::ChannelState),
32 UserRemove(tcp::UserRemove),
33 UserState(tcp::UserState),
34 BanList(tcp::BanList),
35 TextMessage(tcp::TextMessage),
36 PermissionDenied(tcp::PermissionDenied),
37 Acl(tcp::Acl),
38 QueryUsers(tcp::QueryUsers),
39 CryptSetup(tcp::CryptSetup),
40 ContextActionModify(tcp::ContextActionModify),
41 ContextAction(tcp::ContextAction),
42 UserList(tcp::UserList),
43 VoiceTarget(tcp::VoiceTarget),
44 PermissionQuery(tcp::PermissionQuery),
45 CodecVersion(tcp::CodecVersion),
46 UserStats(tcp::UserStats),
47 RequestBlob(tcp::RequestBlob),
48 ServerConfig(tcp::ServerConfig),
49 SuggestConfig(tcp::SuggestConfig),
50 PluginDataTransmission(tcp::PluginDataTransmission),
51}
52
53#[derive(Debug, Error)]
55pub enum DecodeError {
56 #[error(transparent)]
58 Framing(#[from] FramingError),
59
60 #[error("protobuf decode failed for {message_type:?} ({payload_len} bytes): {source}")]
62 Protobuf {
63 message_type: TcpMessageType,
64 payload_len: usize,
65 source: prost::DecodeError,
66 },
67}
68
69pub fn decode_control(message_type: u16, payload: &[u8]) -> Result<ControlMessage, DecodeError> {
74 let message_type = TcpMessageType::try_from(message_type)?;
75 Ok(match message_type {
76 TcpMessageType::UdpTunnel => ControlMessage::UdpTunnel(payload.to_vec()),
78
79 TcpMessageType::Version => ControlMessage::Version(decode_pb(message_type, payload)?),
80 TcpMessageType::Authenticate => {
81 ControlMessage::Authenticate(decode_pb(message_type, payload)?)
82 }
83 TcpMessageType::Ping => ControlMessage::Ping(decode_pb(message_type, payload)?),
84 TcpMessageType::Reject => ControlMessage::Reject(decode_pb(message_type, payload)?),
85 TcpMessageType::ServerSync => ControlMessage::ServerSync(decode_pb(message_type, payload)?),
86 TcpMessageType::ChannelRemove => {
87 ControlMessage::ChannelRemove(decode_pb(message_type, payload)?)
88 }
89 TcpMessageType::ChannelState => {
90 ControlMessage::ChannelState(decode_pb(message_type, payload)?)
91 }
92 TcpMessageType::UserRemove => ControlMessage::UserRemove(decode_pb(message_type, payload)?),
93 TcpMessageType::UserState => ControlMessage::UserState(decode_pb(message_type, payload)?),
94 TcpMessageType::BanList => ControlMessage::BanList(decode_pb(message_type, payload)?),
95 TcpMessageType::TextMessage => {
96 ControlMessage::TextMessage(decode_pb(message_type, payload)?)
97 }
98 TcpMessageType::PermissionDenied => {
99 ControlMessage::PermissionDenied(decode_pb(message_type, payload)?)
100 }
101 TcpMessageType::Acl => ControlMessage::Acl(decode_pb(message_type, payload)?),
102 TcpMessageType::QueryUsers => ControlMessage::QueryUsers(decode_pb(message_type, payload)?),
103 TcpMessageType::CryptSetup => ControlMessage::CryptSetup(decode_pb(message_type, payload)?),
104 TcpMessageType::ContextActionModify => {
105 ControlMessage::ContextActionModify(decode_pb(message_type, payload)?)
106 }
107 TcpMessageType::ContextAction => {
108 ControlMessage::ContextAction(decode_pb(message_type, payload)?)
109 }
110 TcpMessageType::UserList => ControlMessage::UserList(decode_pb(message_type, payload)?),
111 TcpMessageType::VoiceTarget => {
112 ControlMessage::VoiceTarget(decode_pb(message_type, payload)?)
113 }
114 TcpMessageType::PermissionQuery => {
115 ControlMessage::PermissionQuery(decode_pb(message_type, payload)?)
116 }
117 TcpMessageType::CodecVersion => {
118 ControlMessage::CodecVersion(decode_pb(message_type, payload)?)
119 }
120 TcpMessageType::UserStats => ControlMessage::UserStats(decode_pb(message_type, payload)?),
121 TcpMessageType::RequestBlob => {
122 ControlMessage::RequestBlob(decode_pb(message_type, payload)?)
123 }
124 TcpMessageType::ServerConfig => {
125 ControlMessage::ServerConfig(decode_pb(message_type, payload)?)
126 }
127 TcpMessageType::SuggestConfig => {
128 ControlMessage::SuggestConfig(decode_pb(message_type, payload)?)
129 }
130 TcpMessageType::PluginDataTransmission => {
131 ControlMessage::PluginDataTransmission(decode_pb(message_type, payload)?)
132 }
133 })
134}
135
136pub fn decode_frame(frame: &Frame<'_>) -> Result<ControlMessage, DecodeError> {
138 decode_control(frame.message_type, frame.payload)
139}
140
141pub fn encode_control(message: &ControlMessage) -> (u16, Vec<u8>) {
148 match message {
149 ControlMessage::UdpTunnel(bytes) => (u16::from(TcpMessageType::UdpTunnel), bytes.clone()),
151
152 ControlMessage::Version(m) => encode_pb(TcpMessageType::Version, m),
153 ControlMessage::Authenticate(m) => encode_pb(TcpMessageType::Authenticate, m),
154 ControlMessage::Ping(m) => encode_pb(TcpMessageType::Ping, m),
155 ControlMessage::Reject(m) => encode_pb(TcpMessageType::Reject, m),
156 ControlMessage::ServerSync(m) => encode_pb(TcpMessageType::ServerSync, m),
157 ControlMessage::ChannelRemove(m) => encode_pb(TcpMessageType::ChannelRemove, m),
158 ControlMessage::ChannelState(m) => encode_pb(TcpMessageType::ChannelState, m),
159 ControlMessage::UserRemove(m) => encode_pb(TcpMessageType::UserRemove, m),
160 ControlMessage::UserState(m) => encode_pb(TcpMessageType::UserState, m),
161 ControlMessage::BanList(m) => encode_pb(TcpMessageType::BanList, m),
162 ControlMessage::TextMessage(m) => encode_pb(TcpMessageType::TextMessage, m),
163 ControlMessage::PermissionDenied(m) => encode_pb(TcpMessageType::PermissionDenied, m),
164 ControlMessage::Acl(m) => encode_pb(TcpMessageType::Acl, m),
165 ControlMessage::QueryUsers(m) => encode_pb(TcpMessageType::QueryUsers, m),
166 ControlMessage::CryptSetup(m) => encode_pb(TcpMessageType::CryptSetup, m),
167 ControlMessage::ContextActionModify(m) => encode_pb(TcpMessageType::ContextActionModify, m),
168 ControlMessage::ContextAction(m) => encode_pb(TcpMessageType::ContextAction, m),
169 ControlMessage::UserList(m) => encode_pb(TcpMessageType::UserList, m),
170 ControlMessage::VoiceTarget(m) => encode_pb(TcpMessageType::VoiceTarget, m),
171 ControlMessage::PermissionQuery(m) => encode_pb(TcpMessageType::PermissionQuery, m),
172 ControlMessage::CodecVersion(m) => encode_pb(TcpMessageType::CodecVersion, m),
173 ControlMessage::UserStats(m) => encode_pb(TcpMessageType::UserStats, m),
174 ControlMessage::RequestBlob(m) => encode_pb(TcpMessageType::RequestBlob, m),
175 ControlMessage::ServerConfig(m) => encode_pb(TcpMessageType::ServerConfig, m),
176 ControlMessage::SuggestConfig(m) => encode_pb(TcpMessageType::SuggestConfig, m),
177 ControlMessage::PluginDataTransmission(m) => {
178 encode_pb(TcpMessageType::PluginDataTransmission, m)
179 }
180 }
181}
182
183pub fn encode_frame(message: &ControlMessage, out: &mut Vec<u8>) -> Result<(), FramingError> {
187 let (message_type, payload) = encode_control(message);
188 write_frame(message_type, &payload, out)
189}
190
191fn encode_pb<M: Message>(message_type: TcpMessageType, message: &M) -> (u16, Vec<u8>) {
193 (u16::from(message_type), message.encode_to_vec())
194}
195
196fn decode_pb<M: Message + Default>(
199 message_type: TcpMessageType,
200 payload: &[u8],
201) -> Result<M, DecodeError> {
202 M::decode(payload).map_err(|source| DecodeError::Protobuf {
203 message_type,
204 payload_len: payload.len(),
205 source,
206 })
207}
208
209#[cfg(test)]
210mod tests {
211 #![allow(clippy::expect_used)]
213
214 use super::*;
215
216 #[test]
217 fn decodes_a_normal_protobuf_message() {
218 let version = tcp::Version {
219 release: Some("mumble-server-runtime".to_string()),
220 ..Default::default()
221 };
222 let decoded = decode_control(0, &version.encode_to_vec()).expect("decode Version");
223 assert_eq!(decoded, ControlMessage::Version(version));
224 }
225
226 #[test]
227 fn udp_tunnel_payload_is_returned_raw() {
228 let raw_audio = vec![0x80u8, 0x00, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF];
230 let decoded = decode_control(1, &raw_audio).expect("decode UDPTunnel");
231 assert_eq!(decoded, ControlMessage::UdpTunnel(raw_audio));
232 }
233
234 #[test]
235 fn udp_tunnel_is_raw_even_when_bytes_are_valid_protobuf() {
236 let looks_like_protobuf = tcp::UdpTunnel {
241 packet: vec![0xDE, 0xAD, 0xBE, 0xEF],
242 }
243 .encode_to_vec();
244 let decoded = decode_control(1, &looks_like_protobuf).expect("decode UDPTunnel");
245 assert_eq!(decoded, ControlMessage::UdpTunnel(looks_like_protobuf));
246 }
247
248 #[test]
249 fn unknown_type_code_fails_closed() {
250 match decode_control(99, &[]) {
251 Err(DecodeError::Framing(FramingError::UnknownMessageType(99))) => {}
252 other => panic!("expected unknown-type error, got {other:?}"),
253 }
254 }
255
256 #[test]
257 fn encode_then_decode_roundtrips_a_protobuf_message() {
258 let message = ControlMessage::ServerSync(tcp::ServerSync {
259 session: Some(42),
260 max_bandwidth: Some(72_000),
261 welcome_text: Some("hi".to_string()),
262 permissions: Some(0),
263 });
264 let (message_type, payload) = encode_control(&message);
265 assert_eq!(
266 decode_control(message_type, &payload).expect("re-decode"),
267 message
268 );
269 }
270
271 #[test]
272 fn encode_preserves_udp_tunnel_raw_bytes() {
273 let raw_audio = vec![0x00u8, 0x18, 0x02, 0x20, 0xB0, 0x05, 0xDE, 0xAD];
276 let message = ControlMessage::UdpTunnel(raw_audio.clone());
277 let (message_type, payload) = encode_control(&message);
278 assert_eq!(message_type, u16::from(TcpMessageType::UdpTunnel));
279 assert_eq!(payload, raw_audio);
280 assert_eq!(
281 decode_control(message_type, &payload).expect("re-decode"),
282 message
283 );
284 }
285
286 #[test]
287 fn encode_frame_then_parse_roundtrips() {
288 let message = ControlMessage::Version(tcp::Version {
289 release: Some("mumble-server-runtime".to_string()),
290 ..Default::default()
291 });
292 let mut framed = Vec::new();
293 encode_frame(&message, &mut framed).expect("encode frame");
294 let frame = crate::framing::parse_frame(&framed)
295 .expect("parse")
296 .expect("a complete frame");
297 assert_eq!(decode_frame(&frame).expect("decode frame"), message);
298 }
299
300 #[test]
301 fn malformed_protobuf_is_an_error_not_a_default() {
302 let malformed = [0x08u8]; match decode_control(0, &malformed) {
306 Err(DecodeError::Protobuf {
307 message_type: TcpMessageType::Version,
308 ..
309 }) => {}
310 other => panic!("expected protobuf decode error, got {other:?}"),
311 }
312 }
313}