Commit progress
This commit is contained in:
commit
61ee6fe05b
9 changed files with 1935 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
/target
|
||||||
|
.idea
|
||||||
|
.code
|
||||||
|
.vscode
|
1023
Cargo.lock
generated
Normal file
1023
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
18
Cargo.toml
Normal file
18
Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
[package]
|
||||||
|
name = "mumble-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.86"
|
||||||
|
bytes = "1"
|
||||||
|
prost = "0.13"
|
||||||
|
tokio = { version = "1.38", features = ["full"] }
|
||||||
|
tokio-stream = "0.1"
|
||||||
|
tokio-util = { version = "0.7", features = ["codec"] }
|
||||||
|
tokio-rustls = { version = "0.26" }
|
||||||
|
webpki-roots = "0.26"
|
||||||
|
rustls-pki-types = { version = "1.7.0" , features = ["alloc", "std"]}
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
prost-build = "0.13"
|
4
build.rs
Normal file
4
build.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
fn main() {
|
||||||
|
prost_build::compile_protos(&["src/protos/Mumble.proto", "src/protos/MumbleUDP.proto"], &["src/protos"])
|
||||||
|
.expect("Could not build protobuf files")
|
||||||
|
}
|
47
src/codec.rs
Normal file
47
src/codec.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
use anyhow::bail;
|
||||||
|
use bytes::{Buf, BytesMut};
|
||||||
|
use prost::Message;
|
||||||
|
use tokio_util::codec::Decoder;
|
||||||
|
use crate::proto::Version;
|
||||||
|
|
||||||
|
pub enum MumbleMessages {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MumbleCodec {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MumbleCodec {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decoder for MumbleCodec {
|
||||||
|
type Item = ();
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||||
|
if src.len() < 6 {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let message_type = u16::from_be_bytes(src[0..2].try_into()?) as usize;
|
||||||
|
let message_length = u32::from_be_bytes(src[2..6].try_into()?) as usize;
|
||||||
|
let message_data = &src[6..message_length];
|
||||||
|
|
||||||
|
match message_type {
|
||||||
|
0 => {
|
||||||
|
//TODO: Version
|
||||||
|
let version = Version::decode(message_data);
|
||||||
|
println!("{version:?}");
|
||||||
|
Ok(Some(()))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
eprintln!("Unknown message type {:?}", message_type);
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
125
src/lib.rs
Normal file
125
src/lib.rs
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
pub mod codec;
|
||||||
|
|
||||||
|
pub mod proto {
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/mumble_proto.rs"));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod udp {
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/mumble_udp.rs"));
|
||||||
|
}
|
||||||
|
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use tokio::net::TcpStream;
|
||||||
|
use tokio::io::AsyncWriteExt;
|
||||||
|
use tokio_stream::StreamExt;
|
||||||
|
use prost::Message;
|
||||||
|
use tokio_rustls::rustls::{ClientConfig, DigitallySignedStruct, Error, RootCertStore, SignatureScheme};
|
||||||
|
use tokio_rustls::client::TlsStream;
|
||||||
|
use tokio_rustls::{TlsConnector};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use rustls_pki_types::{CertificateDer, ServerName, UnixTime};
|
||||||
|
use tokio_util::codec::FramedRead;
|
||||||
|
use crate::codec::MumbleCodec;
|
||||||
|
use bytes::BytesMut;
|
||||||
|
use tokio_rustls::rustls::client::danger::{DangerousClientConfig, HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
|
||||||
|
|
||||||
|
pub struct MumbleClient {
|
||||||
|
host: SocketAddr,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct NoVerifier;
|
||||||
|
|
||||||
|
impl Debug for NoVerifier {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServerCertVerifier for NoVerifier {
|
||||||
|
fn verify_server_cert(&self, end_entity: &CertificateDer<'_>, intermediates: &[CertificateDer<'_>], server_name: &ServerName<'_>, ocsp_response: &[u8], now: UnixTime) -> Result<ServerCertVerified, Error> {
|
||||||
|
Ok(ServerCertVerified::assertion())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_tls12_signature(&self, message: &[u8], cert: &CertificateDer<'_>, dss: &DigitallySignedStruct) -> Result<HandshakeSignatureValid, Error> {
|
||||||
|
Ok(HandshakeSignatureValid::assertion())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_tls13_signature(&self, message: &[u8], cert: &CertificateDer<'_>, dss: &DigitallySignedStruct) -> Result<HandshakeSignatureValid, Error> {
|
||||||
|
Ok(HandshakeSignatureValid::assertion())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
|
||||||
|
vec![
|
||||||
|
SignatureScheme::RSA_PKCS1_SHA1,
|
||||||
|
SignatureScheme::ECDSA_SHA1_Legacy,
|
||||||
|
SignatureScheme::RSA_PKCS1_SHA256,
|
||||||
|
SignatureScheme::ECDSA_NISTP256_SHA256,
|
||||||
|
SignatureScheme::RSA_PKCS1_SHA384,
|
||||||
|
SignatureScheme::ECDSA_NISTP384_SHA384,
|
||||||
|
SignatureScheme::RSA_PKCS1_SHA512,
|
||||||
|
SignatureScheme::ECDSA_NISTP521_SHA512,
|
||||||
|
SignatureScheme::RSA_PSS_SHA256,
|
||||||
|
SignatureScheme::RSA_PSS_SHA384,
|
||||||
|
SignatureScheme::RSA_PSS_SHA512,
|
||||||
|
SignatureScheme::ED25519,
|
||||||
|
SignatureScheme::ED448,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MumbleClient {
|
||||||
|
pub fn new(host: SocketAddr) -> Self {
|
||||||
|
Self {
|
||||||
|
host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_tcp_connection(&mut self) -> anyhow::Result<TlsStream<TcpStream>> {
|
||||||
|
let mut root_cert_store = RootCertStore::empty();
|
||||||
|
root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
|
||||||
|
|
||||||
|
let mut config = ClientConfig::builder()
|
||||||
|
.with_root_certificates(root_cert_store)
|
||||||
|
.with_no_client_auth();
|
||||||
|
|
||||||
|
config
|
||||||
|
.dangerous()
|
||||||
|
.set_certificate_verifier(Arc::new(NoVerifier));
|
||||||
|
|
||||||
|
let connector = TlsConnector::from(Arc::new(config));
|
||||||
|
let dnsname = ServerName::try_from("127.0.0.1")?;
|
||||||
|
let stream = TcpStream::connect(&self.host).await?;
|
||||||
|
Ok(connector.connect(dnsname, stream).await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn connect(&mut self) -> anyhow::Result<()> {
|
||||||
|
let tcp_stream = self.create_tcp_connection().await?;
|
||||||
|
let mut framed_reader = FramedRead::new(tcp_stream, MumbleCodec::new());
|
||||||
|
let mut message_buffer: BytesMut = BytesMut::new();
|
||||||
|
let version = (proto::Version {
|
||||||
|
os: Some(String::from("Linux")),
|
||||||
|
os_version: None,
|
||||||
|
release: None,
|
||||||
|
version_v1: None,
|
||||||
|
version_v2: None
|
||||||
|
}).encode(&mut message_buffer)?;
|
||||||
|
|
||||||
|
// let mut complete_buffer = BytesMut::new();
|
||||||
|
// complete_buffer.extend((0u16).to_be_bytes());
|
||||||
|
// complete_buffer.extend(((2+4+message_buffer.len()) as u32).to_be_bytes());
|
||||||
|
// complete_buffer.extend(&message_buffer);
|
||||||
|
|
||||||
|
// println!("{complete_buffer:?}");
|
||||||
|
// tcp_stream.write_all(&complete_buffer).await?;
|
||||||
|
|
||||||
|
while let Some(frame_result) = framed_reader.next().await {
|
||||||
|
if let Ok(data) = frame_result {
|
||||||
|
println!("{data:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
8
src/main.rs
Normal file
8
src/main.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
use mumble_rs::MumbleClient;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
let mut client = MumbleClient::new("127.0.0.1:64738".parse()?);
|
||||||
|
client.connect().await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
621
src/protos/Mumble.proto
Normal file
621
src/protos/Mumble.proto
Normal file
|
@ -0,0 +1,621 @@
|
||||||
|
// Copyright 2009-2023 The Mumble Developers. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the LICENSE file at the root of the
|
||||||
|
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
|
||||||
|
|
||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
package MumbleProto;
|
||||||
|
|
||||||
|
option optimize_for = SPEED;
|
||||||
|
|
||||||
|
message Version {
|
||||||
|
// Legacy version number format.
|
||||||
|
optional uint32 version_v1 = 1;
|
||||||
|
// New version number format.
|
||||||
|
// Necessary since patch level may exceed 255. (See https://github.com/mumble-voip/mumble/issues/5827)
|
||||||
|
optional uint64 version_v2 = 5;
|
||||||
|
|
||||||
|
// Client release name.
|
||||||
|
optional string release = 2;
|
||||||
|
// Client OS name.
|
||||||
|
optional string os = 3;
|
||||||
|
// Client OS version.
|
||||||
|
optional string os_version = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not used. Not even for tunneling UDP through TCP.
|
||||||
|
message UDPTunnel {
|
||||||
|
// Not used.
|
||||||
|
required bytes packet = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used by the client to send the authentication credentials to the server.
|
||||||
|
message Authenticate {
|
||||||
|
// UTF-8 encoded username.
|
||||||
|
optional string username = 1;
|
||||||
|
// Server or user password.
|
||||||
|
optional string password = 2;
|
||||||
|
// Additional access tokens for server ACL groups.
|
||||||
|
repeated string tokens = 3;
|
||||||
|
// A list of CELT bitstream version constants supported by the client.
|
||||||
|
repeated int32 celt_versions = 4;
|
||||||
|
optional bool opus = 5 [default = false];
|
||||||
|
// 0 = REGULAR, 1 = BOT
|
||||||
|
optional int32 client_type = 6 [default = 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sent by the client to notify the server that the client is still alive.
|
||||||
|
// Server must reply to the packet with the same timestamp and its own
|
||||||
|
// good/late/lost/resync numbers. None of the fields is strictly required.
|
||||||
|
message Ping {
|
||||||
|
// Client timestamp. Server should not attempt to decode.
|
||||||
|
optional uint64 timestamp = 1;
|
||||||
|
// The amount of good packets received.
|
||||||
|
optional uint32 good = 2;
|
||||||
|
// The amount of late packets received.
|
||||||
|
optional uint32 late = 3;
|
||||||
|
// The amount of packets never received.
|
||||||
|
optional uint32 lost = 4;
|
||||||
|
// The amount of nonce resyncs.
|
||||||
|
optional uint32 resync = 5;
|
||||||
|
// The total amount of UDP packets received.
|
||||||
|
optional uint32 udp_packets = 6;
|
||||||
|
// The total amount of TCP packets received.
|
||||||
|
optional uint32 tcp_packets = 7;
|
||||||
|
// UDP ping average.
|
||||||
|
optional float udp_ping_avg = 8;
|
||||||
|
// UDP ping variance.
|
||||||
|
optional float udp_ping_var = 9;
|
||||||
|
// TCP ping average.
|
||||||
|
optional float tcp_ping_avg = 10;
|
||||||
|
// TCP ping variance.
|
||||||
|
optional float tcp_ping_var = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sent by the server when it rejects the user connection.
|
||||||
|
message Reject {
|
||||||
|
enum RejectType {
|
||||||
|
// The rejection reason is unknown (details should be available
|
||||||
|
// in Reject.reason).
|
||||||
|
None = 0;
|
||||||
|
// The client attempted to connect with an incompatible version.
|
||||||
|
WrongVersion = 1;
|
||||||
|
// The user name supplied by the client was invalid.
|
||||||
|
InvalidUsername = 2;
|
||||||
|
// The client attempted to authenticate as a user with a password but it
|
||||||
|
// was wrong.
|
||||||
|
WrongUserPW = 3;
|
||||||
|
// The client attempted to connect to a passworded server but the password
|
||||||
|
// was wrong.
|
||||||
|
WrongServerPW = 4;
|
||||||
|
// Supplied username is already in use.
|
||||||
|
UsernameInUse = 5;
|
||||||
|
// Server is currently full and cannot accept more users.
|
||||||
|
ServerFull = 6;
|
||||||
|
// The user did not provide a certificate but one is required.
|
||||||
|
NoCertificate = 7;
|
||||||
|
AuthenticatorFail = 8;
|
||||||
|
}
|
||||||
|
// Rejection type.
|
||||||
|
optional RejectType type = 1;
|
||||||
|
// Human readable rejection reason.
|
||||||
|
optional string reason = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerSync message is sent by the server when it has authenticated the user
|
||||||
|
// and finished synchronizing the server state.
|
||||||
|
message ServerSync {
|
||||||
|
// The session of the current user.
|
||||||
|
optional uint32 session = 1;
|
||||||
|
// Maximum bandwidth that the user should use.
|
||||||
|
optional uint32 max_bandwidth = 2;
|
||||||
|
// Server welcome text.
|
||||||
|
optional string welcome_text = 3;
|
||||||
|
// Current user permissions in the root channel.
|
||||||
|
// Note: The permissions data type usually is uin32 (e.g. in PermissionQuery and PermissionDenied messages). Here
|
||||||
|
// it is uint64 because of an oversight in the past. Nonetheless it should never exceed the uin32 range.
|
||||||
|
// See also: https://github.com/mumble-voip/mumble/issues/5139
|
||||||
|
optional uint64 permissions = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sent by the client when it wants a channel removed. Sent by the server when
|
||||||
|
// a channel has been removed and clients should be notified.
|
||||||
|
message ChannelRemove {
|
||||||
|
required uint32 channel_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to communicate channel properties between the client and the server.
|
||||||
|
// Sent by the server during the login process or when channel properties are
|
||||||
|
// updated. Client may use this message to update said channel properties.
|
||||||
|
message ChannelState {
|
||||||
|
// Unique ID for the channel within the server.
|
||||||
|
optional uint32 channel_id = 1;
|
||||||
|
// channel_id of the parent channel.
|
||||||
|
optional uint32 parent = 2;
|
||||||
|
// UTF-8 encoded channel name.
|
||||||
|
optional string name = 3;
|
||||||
|
// A collection of channel id values of the linked channels. Absent during
|
||||||
|
// the first channel listing.
|
||||||
|
repeated uint32 links = 4;
|
||||||
|
// UTF-8 encoded channel description. Only if the description is less than
|
||||||
|
// 128 bytes
|
||||||
|
optional string description = 5;
|
||||||
|
// A collection of channel_id values that should be added to links.
|
||||||
|
repeated uint32 links_add = 6;
|
||||||
|
// A collection of channel_id values that should be removed from links.
|
||||||
|
repeated uint32 links_remove = 7;
|
||||||
|
// True if the channel is temporary.
|
||||||
|
optional bool temporary = 8 [default = false];
|
||||||
|
// Position weight to tweak the channel position in the channel list.
|
||||||
|
optional int32 position = 9 [default = 0];
|
||||||
|
// SHA1 hash of the description if the description is 128 bytes or more.
|
||||||
|
optional bytes description_hash = 10;
|
||||||
|
// Maximum number of users allowed in the channel. If this value is zero,
|
||||||
|
// the maximum number of users allowed in the channel is given by the
|
||||||
|
// server's "usersperchannel" setting.
|
||||||
|
optional uint32 max_users = 11;
|
||||||
|
// Whether this channel has enter restrictions (ACL denying ENTER) set
|
||||||
|
optional bool is_enter_restricted = 12;
|
||||||
|
// Whether the receiver of this msg is considered to be able to enter this channel
|
||||||
|
optional bool can_enter = 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to communicate user leaving or being kicked. May be sent by the client
|
||||||
|
// when it attempts to kick a user. Sent by the server when it informs the
|
||||||
|
// clients that a user is not present anymore.
|
||||||
|
message UserRemove {
|
||||||
|
// The user who is being kicked, identified by their session, not present
|
||||||
|
// when no one is being kicked.
|
||||||
|
required uint32 session = 1;
|
||||||
|
// The user who initiated the removal. Either the user who performs the kick
|
||||||
|
// or the user who is currently leaving.
|
||||||
|
optional uint32 actor = 2;
|
||||||
|
// Reason for the kick, stored as the ban reason if the user is banned.
|
||||||
|
optional string reason = 3;
|
||||||
|
// True if the kick should result in a ban.
|
||||||
|
optional bool ban = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sent by the server when it communicates new and changed users to client.
|
||||||
|
// First seen during login procedure. May be sent by the client when it wishes
|
||||||
|
// to alter its state.
|
||||||
|
message UserState {
|
||||||
|
message VolumeAdjustment {
|
||||||
|
optional uint32 listening_channel = 1;
|
||||||
|
optional float volume_adjustment = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unique user session ID of the user whose state this is, may change on
|
||||||
|
// reconnect.
|
||||||
|
optional uint32 session = 1;
|
||||||
|
// The session of the user who is updating this user.
|
||||||
|
optional uint32 actor = 2;
|
||||||
|
// User name, UTF-8 encoded.
|
||||||
|
optional string name = 3;
|
||||||
|
// Registered user ID if the user is registered.
|
||||||
|
optional uint32 user_id = 4;
|
||||||
|
// Channel on which the user is.
|
||||||
|
optional uint32 channel_id = 5;
|
||||||
|
// True if the user is muted by admin.
|
||||||
|
optional bool mute = 6;
|
||||||
|
// True if the user is deafened by admin.
|
||||||
|
optional bool deaf = 7;
|
||||||
|
// True if the user has been suppressed from talking by a reason other than
|
||||||
|
// being muted.
|
||||||
|
optional bool suppress = 8;
|
||||||
|
// True if the user has muted self.
|
||||||
|
optional bool self_mute = 9;
|
||||||
|
// True if the user has deafened self.
|
||||||
|
optional bool self_deaf = 10;
|
||||||
|
// User image if it is less than 128 bytes.
|
||||||
|
optional bytes texture = 11;
|
||||||
|
// The positional audio plugin identifier.
|
||||||
|
// Positional audio information is only sent to users who share
|
||||||
|
// identical plugin contexts.
|
||||||
|
//
|
||||||
|
// This value is not transmitted to clients.
|
||||||
|
optional bytes plugin_context = 12;
|
||||||
|
// The user's plugin-specific identity.
|
||||||
|
// This value is not transmitted to clients.
|
||||||
|
optional string plugin_identity = 13;
|
||||||
|
// User comment if it is less than 128 bytes.
|
||||||
|
optional string comment = 14;
|
||||||
|
// The hash of the user certificate.
|
||||||
|
optional string hash = 15;
|
||||||
|
// SHA1 hash of the user comment if it 128 bytes or more.
|
||||||
|
optional bytes comment_hash = 16;
|
||||||
|
// SHA1 hash of the user picture if it 128 bytes or more.
|
||||||
|
optional bytes texture_hash = 17;
|
||||||
|
// True if the user is a priority speaker.
|
||||||
|
optional bool priority_speaker = 18;
|
||||||
|
// True if the user is currently recording.
|
||||||
|
optional bool recording = 19;
|
||||||
|
// A list of temporary access tokens to be respected when processing this request.
|
||||||
|
repeated string temporary_access_tokens = 20;
|
||||||
|
// A list of channels the user wants to start listening to.
|
||||||
|
repeated uint32 listening_channel_add = 21;
|
||||||
|
// a list of channels the user does no longer want to listen to.
|
||||||
|
repeated uint32 listening_channel_remove = 22;
|
||||||
|
// A list of volume adjustments the user has applied to listeners
|
||||||
|
repeated VolumeAdjustment listening_volume_adjustment = 23;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relays information on the bans. The client may send the BanList message to
|
||||||
|
// either modify the list of bans or query them from the server. The server
|
||||||
|
// sends this list only after a client queries for it.
|
||||||
|
message BanList {
|
||||||
|
message BanEntry {
|
||||||
|
// Banned IP address.
|
||||||
|
required bytes address = 1;
|
||||||
|
// The length of the subnet mask for the ban.
|
||||||
|
required uint32 mask = 2;
|
||||||
|
// User name for identification purposes (does not affect the ban).
|
||||||
|
optional string name = 3;
|
||||||
|
// The certificate hash of the banned user.
|
||||||
|
optional string hash = 4;
|
||||||
|
// Reason for the ban (does not affect the ban).
|
||||||
|
optional string reason = 5;
|
||||||
|
// Ban start time.
|
||||||
|
optional string start = 6;
|
||||||
|
// Ban duration in seconds.
|
||||||
|
optional uint32 duration = 7;
|
||||||
|
}
|
||||||
|
// List of ban entries currently in place.
|
||||||
|
repeated BanEntry bans = 1;
|
||||||
|
// True if the server should return the list, false if it should replace old
|
||||||
|
// ban list with the one provided.
|
||||||
|
optional bool query = 2 [default = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to send and broadcast text messages.
|
||||||
|
message TextMessage {
|
||||||
|
// The message sender, identified by its session.
|
||||||
|
optional uint32 actor = 1;
|
||||||
|
// Target users for the message, identified by their session.
|
||||||
|
repeated uint32 session = 2;
|
||||||
|
// The channels to which the message is sent, identified by their
|
||||||
|
// channel_ids.
|
||||||
|
repeated uint32 channel_id = 3;
|
||||||
|
// The root channels when sending message recursively to several channels,
|
||||||
|
// identified by their channel_ids.
|
||||||
|
repeated uint32 tree_id = 4;
|
||||||
|
// The UTF-8 encoded message. May be HTML if the server allows.
|
||||||
|
required string message = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PermissionDenied {
|
||||||
|
enum DenyType {
|
||||||
|
// Operation denied for other reason, see reason field.
|
||||||
|
Text = 0;
|
||||||
|
// Permissions were denied.
|
||||||
|
Permission = 1;
|
||||||
|
// Cannot modify SuperUser.
|
||||||
|
SuperUser = 2;
|
||||||
|
// Invalid channel name.
|
||||||
|
ChannelName = 3;
|
||||||
|
// Text message too long.
|
||||||
|
TextTooLong = 4;
|
||||||
|
// The flux capacitor was spelled wrong.
|
||||||
|
H9K = 5;
|
||||||
|
// Operation not permitted in temporary channel.
|
||||||
|
TemporaryChannel = 6;
|
||||||
|
// Operation requires certificate.
|
||||||
|
MissingCertificate = 7;
|
||||||
|
// Invalid username.
|
||||||
|
UserName = 8;
|
||||||
|
// Channel is full.
|
||||||
|
ChannelFull = 9;
|
||||||
|
// Channels are nested too deeply.
|
||||||
|
NestingLimit = 10;
|
||||||
|
// Maximum channel count reached.
|
||||||
|
ChannelCountLimit = 11;
|
||||||
|
// Amount of listener objects for this channel has been reached
|
||||||
|
ChannelListenerLimit = 12;
|
||||||
|
// Amount of listener proxies for the user has been reached
|
||||||
|
UserListenerLimit = 13;
|
||||||
|
}
|
||||||
|
// The denied permission when type is Permission.
|
||||||
|
optional uint32 permission = 1;
|
||||||
|
// channel_id for the channel where the permission was denied when type is
|
||||||
|
// Permission.
|
||||||
|
optional uint32 channel_id = 2;
|
||||||
|
// The user who was denied permissions, identified by session.
|
||||||
|
optional uint32 session = 3;
|
||||||
|
// Textual reason for the denial.
|
||||||
|
optional string reason = 4;
|
||||||
|
// Type of the denial.
|
||||||
|
optional DenyType type = 5;
|
||||||
|
// The name that is invalid when type is UserName.
|
||||||
|
optional string name = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ACL {
|
||||||
|
message ChanGroup {
|
||||||
|
// Name of the channel group, UTF-8 encoded.
|
||||||
|
required string name = 1;
|
||||||
|
// True if the group has been inherited from the parent (Read only).
|
||||||
|
optional bool inherited = 2 [default = true];
|
||||||
|
// True if the group members are inherited.
|
||||||
|
optional bool inherit = 3 [default = true];
|
||||||
|
// True if the group can be inherited by sub channels.
|
||||||
|
optional bool inheritable = 4 [default = true];
|
||||||
|
// Users explicitly included in this group, identified by user_id.
|
||||||
|
repeated uint32 add = 5;
|
||||||
|
// Users explicitly removed from this group in this channel if the group
|
||||||
|
// has been inherited, identified by user_id.
|
||||||
|
repeated uint32 remove = 6;
|
||||||
|
// Users inherited, identified by user_id.
|
||||||
|
repeated uint32 inherited_members = 7;
|
||||||
|
}
|
||||||
|
message ChanACL {
|
||||||
|
// True if this ACL applies to the current channel.
|
||||||
|
optional bool apply_here = 1 [default = true];
|
||||||
|
// True if this ACL applies to the sub channels.
|
||||||
|
optional bool apply_subs = 2 [default = true];
|
||||||
|
// True if the ACL has been inherited from the parent.
|
||||||
|
optional bool inherited = 3 [default = true];
|
||||||
|
// ID of the user that is affected by this ACL.
|
||||||
|
optional uint32 user_id = 4;
|
||||||
|
// ID of the group that is affected by this ACL.
|
||||||
|
optional string group = 5;
|
||||||
|
// Bit flag field of the permissions granted by this ACL.
|
||||||
|
optional uint32 grant = 6;
|
||||||
|
// Bit flag field of the permissions denied by this ACL.
|
||||||
|
optional uint32 deny = 7;
|
||||||
|
}
|
||||||
|
// Channel ID of the channel this message affects.
|
||||||
|
required uint32 channel_id = 1;
|
||||||
|
// True if the channel inherits its parent's ACLs.
|
||||||
|
optional bool inherit_acls = 2 [default = true];
|
||||||
|
// User group specifications.
|
||||||
|
repeated ChanGroup groups = 3;
|
||||||
|
// ACL specifications.
|
||||||
|
repeated ChanACL acls = 4;
|
||||||
|
// True if the message is a query for ACLs instead of setting them.
|
||||||
|
optional bool query = 5 [default = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client may use this message to refresh its registered user information. The
|
||||||
|
// client should fill the IDs or Names of the users it wants to refresh. The
|
||||||
|
// server fills the missing parts and sends the message back.
|
||||||
|
message QueryUsers {
|
||||||
|
// user_ids.
|
||||||
|
repeated uint32 ids = 1;
|
||||||
|
// User names in the same order as ids.
|
||||||
|
repeated string names = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to initialize and resync the UDP encryption. Either side may request a
|
||||||
|
// resync by sending the message without any values filled. The resync is
|
||||||
|
// performed by sending the message with only the client or server nonce
|
||||||
|
// filled.
|
||||||
|
message CryptSetup {
|
||||||
|
// Encryption key.
|
||||||
|
optional bytes key = 1;
|
||||||
|
// Client nonce.
|
||||||
|
optional bytes client_nonce = 2;
|
||||||
|
// Server nonce.
|
||||||
|
optional bytes server_nonce = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to add or remove custom context menu item on client-side.
|
||||||
|
message ContextActionModify {
|
||||||
|
enum Context {
|
||||||
|
// Action is applicable to the server.
|
||||||
|
Server = 0x01;
|
||||||
|
// Action can target a Channel.
|
||||||
|
Channel = 0x02;
|
||||||
|
// Action can target a User.
|
||||||
|
User = 0x04;
|
||||||
|
}
|
||||||
|
enum Operation {
|
||||||
|
Add = 0;
|
||||||
|
Remove = 1;
|
||||||
|
}
|
||||||
|
// The action identifier. Used later to initiate an action.
|
||||||
|
required string action = 1;
|
||||||
|
// The display name of the action.
|
||||||
|
optional string text = 2;
|
||||||
|
// Context bit flags defining where the action should be displayed.
|
||||||
|
// Flags can be OR-ed to combine different types.
|
||||||
|
optional uint32 context = 3;
|
||||||
|
// Choose either to add or to remove the context action.
|
||||||
|
// Note: This field only exists after Mumble 1.2.4-beta1 release.
|
||||||
|
// The message will be recognized as Add regardless of this field
|
||||||
|
// before said release.
|
||||||
|
optional Operation operation = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sent by the client when it wants to initiate a Context action.
|
||||||
|
message ContextAction {
|
||||||
|
// The target User for the action, identified by session.
|
||||||
|
optional uint32 session = 1;
|
||||||
|
// The target Channel for the action, identified by channel_id.
|
||||||
|
optional uint32 channel_id = 2;
|
||||||
|
// The action that should be executed.
|
||||||
|
required string action = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lists the registered users.
|
||||||
|
message UserList {
|
||||||
|
message User {
|
||||||
|
// Registered user ID.
|
||||||
|
required uint32 user_id = 1;
|
||||||
|
// Registered user name.
|
||||||
|
optional string name = 2;
|
||||||
|
optional string last_seen = 3;
|
||||||
|
optional uint32 last_channel = 4;
|
||||||
|
}
|
||||||
|
// A list of registered users.
|
||||||
|
repeated User users = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sent by the client when it wants to register or clear whisper targets.
|
||||||
|
//
|
||||||
|
// Note: The first available target ID is 1 as 0 is reserved for normal
|
||||||
|
// talking. Maximum target ID is 30.
|
||||||
|
message VoiceTarget {
|
||||||
|
message Target {
|
||||||
|
// Users that are included as targets.
|
||||||
|
repeated uint32 session = 1;
|
||||||
|
// Channel that is included as a target.
|
||||||
|
optional uint32 channel_id = 2;
|
||||||
|
// ACL group that is included as a target.
|
||||||
|
optional string group = 3;
|
||||||
|
// True if the voice should follow links from the specified channel.
|
||||||
|
optional bool links = 4 [default = false];
|
||||||
|
// True if the voice should also be sent to children of the specific
|
||||||
|
// channel.
|
||||||
|
optional bool children = 5 [default = false];
|
||||||
|
}
|
||||||
|
// Voice target ID.
|
||||||
|
optional uint32 id = 1;
|
||||||
|
// The receivers that this voice target includes.
|
||||||
|
repeated Target targets = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sent by the client when it wants permissions for a certain channel. Sent by
|
||||||
|
// the server when it replies to the query or wants the user to resync all
|
||||||
|
// channel permissions.
|
||||||
|
message PermissionQuery {
|
||||||
|
// channel_id of the channel for which the permissions are queried.
|
||||||
|
optional uint32 channel_id = 1;
|
||||||
|
// Channel permissions.
|
||||||
|
optional uint32 permissions = 2;
|
||||||
|
// True if the client should drop its current permission information for all
|
||||||
|
// channels.
|
||||||
|
optional bool flush = 3 [default = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sent by the server to notify the users of the version of the CELT codec they
|
||||||
|
// should use. This may change during the connection when new users join.
|
||||||
|
message CodecVersion {
|
||||||
|
// The version of the CELT Alpha codec.
|
||||||
|
required int32 alpha = 1;
|
||||||
|
// The version of the CELT Beta codec.
|
||||||
|
required int32 beta = 2;
|
||||||
|
// True if the user should prefer Alpha over Beta.
|
||||||
|
required bool prefer_alpha = 3 [default = true];
|
||||||
|
optional bool opus = 4 [default = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to communicate user stats between the server and clients.
|
||||||
|
message UserStats {
|
||||||
|
message Stats {
|
||||||
|
// The amount of good packets received.
|
||||||
|
optional uint32 good = 1;
|
||||||
|
// The amount of late packets received.
|
||||||
|
optional uint32 late = 2;
|
||||||
|
// The amount of packets never received.
|
||||||
|
optional uint32 lost = 3;
|
||||||
|
// The amount of nonce resyncs.
|
||||||
|
optional uint32 resync = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// User whose stats these are.
|
||||||
|
optional uint32 session = 1;
|
||||||
|
// True if the message contains only mutable stats (packets, ping).
|
||||||
|
optional bool stats_only = 2 [default = false];
|
||||||
|
// Full user certificate chain of the user certificate in DER format.
|
||||||
|
repeated bytes certificates = 3;
|
||||||
|
// Packet statistics for packets received from the client.
|
||||||
|
optional Stats from_client = 4;
|
||||||
|
// Packet statistics for packets sent by the server.
|
||||||
|
optional Stats from_server = 5;
|
||||||
|
|
||||||
|
// Amount of UDP packets sent.
|
||||||
|
optional uint32 udp_packets = 6;
|
||||||
|
// Amount of TCP packets sent.
|
||||||
|
optional uint32 tcp_packets = 7;
|
||||||
|
// UDP ping average.
|
||||||
|
optional float udp_ping_avg = 8;
|
||||||
|
// UDP ping variance.
|
||||||
|
optional float udp_ping_var = 9;
|
||||||
|
// TCP ping average.
|
||||||
|
optional float tcp_ping_avg = 10;
|
||||||
|
// TCP ping variance.
|
||||||
|
optional float tcp_ping_var = 11;
|
||||||
|
|
||||||
|
// Client version.
|
||||||
|
optional Version version = 12;
|
||||||
|
// A list of CELT bitstream version constants supported by the client of this
|
||||||
|
// user.
|
||||||
|
repeated int32 celt_versions = 13;
|
||||||
|
// Client IP address.
|
||||||
|
optional bytes address = 14;
|
||||||
|
// Bandwidth used by this client.
|
||||||
|
optional uint32 bandwidth = 15;
|
||||||
|
// Connection duration.
|
||||||
|
optional uint32 onlinesecs = 16;
|
||||||
|
// Duration since last activity.
|
||||||
|
optional uint32 idlesecs = 17;
|
||||||
|
// True if the user has a strong certificate.
|
||||||
|
optional bool strong_certificate = 18 [default = false];
|
||||||
|
optional bool opus = 19 [default = false];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used by the client to request binary data from the server. By default large
|
||||||
|
// comments or textures are not sent within standard messages but instead the
|
||||||
|
// hash is. If the client does not recognize the hash it may request the
|
||||||
|
// resource when it needs it. The client does so by sending a RequestBlob
|
||||||
|
// message with the correct fields filled with the user sessions or channel_ids
|
||||||
|
// it wants to receive. The server replies to this by sending a new
|
||||||
|
// UserState/ChannelState message with the resources filled even if they would
|
||||||
|
// normally be transmitted as hashes.
|
||||||
|
message RequestBlob {
|
||||||
|
// sessions of the requested UserState textures.
|
||||||
|
repeated uint32 session_texture = 1;
|
||||||
|
// sessions of the requested UserState comments.
|
||||||
|
repeated uint32 session_comment = 2;
|
||||||
|
// channel_ids of the requested ChannelState descriptions.
|
||||||
|
repeated uint32 channel_description = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sent by the server when it informs the clients on server configuration
|
||||||
|
// details.
|
||||||
|
message ServerConfig {
|
||||||
|
// The maximum bandwidth the clients should use.
|
||||||
|
optional uint32 max_bandwidth = 1;
|
||||||
|
// Server welcome text.
|
||||||
|
optional string welcome_text = 2;
|
||||||
|
// True if the server allows HTML.
|
||||||
|
optional bool allow_html = 3;
|
||||||
|
// Maximum text message length.
|
||||||
|
optional uint32 message_length = 4;
|
||||||
|
// Maximum image message length.
|
||||||
|
optional uint32 image_message_length = 5;
|
||||||
|
// The maximum number of users allowed on the server.
|
||||||
|
optional uint32 max_users = 6;
|
||||||
|
// Whether using Mumble's recording feature is allowed on the server
|
||||||
|
optional bool recording_allowed = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sent by the server to inform the clients of suggested client configuration
|
||||||
|
// specified by the server administrator.
|
||||||
|
message SuggestConfig {
|
||||||
|
// Suggested client version in the legacy format.
|
||||||
|
optional uint32 version_v1 = 1;
|
||||||
|
// Suggested client version in the new format.
|
||||||
|
// Necessary since patch level may exceed 255. (See https://github.com/mumble-voip/mumble/issues/5827)
|
||||||
|
optional uint64 version_v2 = 4;
|
||||||
|
|
||||||
|
// True if the administrator suggests positional audio to be used on this
|
||||||
|
// server.
|
||||||
|
optional bool positional = 2;
|
||||||
|
// True if the administrator suggests push to talk to be used on this server.
|
||||||
|
optional bool push_to_talk = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to send plugin messages between clients
|
||||||
|
message PluginDataTransmission {
|
||||||
|
// The session ID of the client this message was sent from
|
||||||
|
optional uint32 senderSession = 1;
|
||||||
|
// The session IDs of the clients that should receive this message
|
||||||
|
repeated uint32 receiverSessions = 2 [packed = true];
|
||||||
|
// The data that is sent
|
||||||
|
optional bytes data = 3;
|
||||||
|
// The ID of the sent data. This will be used by plugins to check whether they will
|
||||||
|
// process it or not
|
||||||
|
optional string dataID = 4;
|
||||||
|
}
|
85
src/protos/MumbleUDP.proto
Normal file
85
src/protos/MumbleUDP.proto
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
// Copyright 2022-2023 The Mumble Developers. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the LICENSE file at the root of the
|
||||||
|
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package MumbleUDP;
|
||||||
|
|
||||||
|
option optimize_for = SPEED;
|
||||||
|
|
||||||
|
message Audio {
|
||||||
|
oneof Header {
|
||||||
|
// When this audio is sent by the client to the server, this is set to the target of the audio data. This target
|
||||||
|
// is a number in the range [0, 2^{32} - 1], where 0 means "normal talking", 2^{5} - 1 means "server loopback"
|
||||||
|
// and all other targets are understood as shout/whisper targets that have previously been registered via a
|
||||||
|
// VoiceTarget message (via TCP).
|
||||||
|
uint32 target = 1;
|
||||||
|
// When this audio is sent by the server to the client, this indicates the context in which the audio has been sent.
|
||||||
|
// 0: Normal speech
|
||||||
|
// 1: Shout to channel
|
||||||
|
// 2: Whisper to user
|
||||||
|
// 3: Received via channel listener
|
||||||
|
uint32 context = 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The session of the client (sender) this audio was originally sent from. This field is not required when sending
|
||||||
|
// audio to the server, but will always be set when receiving audio from the server.
|
||||||
|
uint32 sender_session = 3;
|
||||||
|
|
||||||
|
// The number of the first contained audio frame (indicating the position of that frame in the overall audio stream)
|
||||||
|
uint64 frame_number = 4;
|
||||||
|
|
||||||
|
// The actual voice data payload in the Opus format.
|
||||||
|
bytes opus_data = 5;
|
||||||
|
|
||||||
|
// Optional positional data indicating the speaker's position in a virtual world (in meters). This "list" is really
|
||||||
|
// expected to be an array of size 3 containing the X, Y and Z coordinates of the position (in that order).
|
||||||
|
repeated float positional_data = 6;
|
||||||
|
|
||||||
|
// A volume adjustment determined by the server for this audio packet. It is up to the client to apply this adjustment to
|
||||||
|
// the resulting audio (or not). Note: A value of 0 means that this field is unset.
|
||||||
|
float volume_adjustment = 7;
|
||||||
|
|
||||||
|
// Note that we skip the field indices up to (including) 15 in order to have them available for future extensions of the
|
||||||
|
// protocol with fields that are encountered very often. The reason is that all field indices <= 15 require only a single
|
||||||
|
// byte of encoding overhead, whereas the once > 15 require (at least) two bytes. The reason lies in the Protobuf encoding
|
||||||
|
// scheme that uses 1 bit for a varint continuation flag, 3 bit to encode a field's type and the remaining 4 bit of the
|
||||||
|
// first byte are thus available for the field index. Therefore the first 2^4 = 16 field indices (aka values 0 to 15) can
|
||||||
|
// be encoded using only a single byte. For details see https://developers.google.com/protocol-buffers/docs/encoding
|
||||||
|
|
||||||
|
// A flag indicating whether this audio packet represents the end of transmission for the current audio stream
|
||||||
|
bool is_terminator = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ping message for checking UDP connectivity (and roundtrip ping) and potentially obtaining further server
|
||||||
|
* details (e.g. version).
|
||||||
|
*/
|
||||||
|
message Ping {
|
||||||
|
// Timestamp as encoded by the client. A server is not supposed to attempt to decode or modify this field. Therefore,
|
||||||
|
// clients may choose an arbitrary format for this timestamp (as long as it fits into a uint64 field).
|
||||||
|
uint64 timestamp = 1;
|
||||||
|
|
||||||
|
// A flag set by the sending client, if it wants to obtain additional information about the server.
|
||||||
|
bool request_extended_information = 2;
|
||||||
|
|
||||||
|
|
||||||
|
// Below are the fields for the "additional information" that are filled out by the server on request.
|
||||||
|
|
||||||
|
// The version of the server in the new version format.
|
||||||
|
// The new protobuf Ping packet introduced with 1.5 drops support for the legacy version format
|
||||||
|
// since both server and client have to support this new format.
|
||||||
|
// (See https://github.com/mumble-voip/mumble/issues/5827)
|
||||||
|
uint64 server_version_v2 = 3;
|
||||||
|
|
||||||
|
// The amount of users currently connected to the server
|
||||||
|
uint32 user_count = 4;
|
||||||
|
|
||||||
|
// The maximum amount of users permitted on this server
|
||||||
|
uint32 max_user_count = 5;
|
||||||
|
|
||||||
|
// The maximum bandwidth each user is allowed to use for sending audio to the server
|
||||||
|
uint32 max_bandwidth_per_user = 6;
|
||||||
|
}
|
Loading…
Reference in a new issue