Compare commits

...

7 commits
master ... main

Author SHA1 Message Date
Leon Grünewald
838f9188b3 Remove print, use port on UDP 2024-09-17 02:40:00 +02:00
Leon Grünewald
6ff29972c7 Add license and description to Cargo.toml 2024-09-17 02:05:56 +02:00
Leon Grünewald
57c048ab83 Cargo Format 2024-09-17 02:01:00 +02:00
Leon Grünewald
2b1e16e1cb Add missing messages and clean up imports 2024-09-17 02:00:49 +02:00
Leon Grünewald
145181e364 Remove main.rs 2024-09-17 00:28:48 +02:00
Leon Grünewald
ff9b2d3f96 Commit new messages and fix message length mistake 2024-09-17 00:24:35 +02:00
Leon Grünewald
9865c10f40 Add more messages 2024-09-16 17:20:41 +02:00
6 changed files with 398 additions and 85 deletions

95
Cargo.lock generated
View file

@ -206,18 +206,95 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
[[package]]
name = "futures"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]] [[package]]
name = "futures-core" name = "futures-core"
version = "0.3.30" version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-executor"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
[[package]]
name = "futures-macro"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "futures-sink" name = "futures-sink"
version = "0.3.30" version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
[[package]]
name = "futures-task"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]]
name = "futures-util"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.15" version = "0.2.15"
@ -401,10 +478,11 @@ checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
[[package]] [[package]]
name = "mumble-rs" name = "mumble-rs"
version = "0.1.0" version = "0.1.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
"futures",
"prost", "prost",
"prost-build", "prost-build",
"rustls-pki-types", "rustls-pki-types",
@ -495,6 +573,12 @@ version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]] [[package]]
name = "prettyplease" name = "prettyplease"
version = "0.2.20" version = "0.2.20"
@ -708,6 +792,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.13.2" version = "1.13.2"

View file

@ -1,7 +1,9 @@
[package] [package]
name = "mumble-rs" name = "mumble-rs"
version = "0.1.0" version = "0.1.1"
edition = "2021" edition = "2021"
license = "MIT"
description = "A small simple crate to communicate with a mumble server"
[dependencies] [dependencies]
anyhow = "1.0.86" anyhow = "1.0.86"
@ -13,6 +15,7 @@ tokio-util = { version = "0.7", features = ["codec"] }
tokio-rustls = { version = "0.26" } tokio-rustls = { version = "0.26" }
webpki-roots = "0.26" webpki-roots = "0.26"
rustls-pki-types = { version = "1.7.0" , features = ["alloc", "std"]} rustls-pki-types = { version = "1.7.0" , features = ["alloc", "std"]}
futures = "0.3.30"
[build-dependencies] [build-dependencies]
prost-build = "0.13" prost-build = "0.13"

View file

@ -1,4 +1,7 @@
fn main() { fn main() {
prost_build::compile_protos(&["src/protos/Mumble.proto", "src/protos/MumbleUDP.proto"], &["src/protos"]) prost_build::compile_protos(
.expect("Could not build protobuf files") &["src/protos/Mumble.proto", "src/protos/MumbleUDP.proto"],
&["src/protos"],
)
.expect("Could not build protobuf files")
} }

View file

@ -1,25 +1,20 @@
use crate::proto::*;
use crate::MumbleMessage;
use anyhow::bail; use anyhow::bail;
use bytes::{Buf, BytesMut}; use bytes::{Buf, BufMut, BytesMut};
use prost::Message; use prost::Message;
use tokio_util::codec::Decoder; use tokio_util::codec::{Decoder, Encoder};
use crate::proto::Version;
pub enum MumbleMessages { pub struct MumbleTcpCodec {}
} impl MumbleTcpCodec {
pub struct MumbleCodec {
}
impl MumbleCodec {
pub fn new() -> Self { pub fn new() -> Self {
Self {} Self {}
} }
} }
impl Decoder for MumbleCodec { impl Decoder for MumbleTcpCodec {
type Item = (); type Item = MumbleMessage;
type Error = anyhow::Error; type Error = anyhow::Error;
fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> { fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
@ -27,21 +22,183 @@ impl Decoder for MumbleCodec {
return Ok(None); return Ok(None);
} }
let message_type = u16::from_be_bytes(src[0..2].try_into()?) as usize; let (message_type, rest) = src.split_at(2);
let message_length = u32::from_be_bytes(src[2..6].try_into()?) as usize; let (message_length, rest) = rest.split_at(4);
let message_data = &src[6..message_length]; let message_type = u16::from_be_bytes(message_type.try_into()?) as usize;
let message_length = u32::from_be_bytes(message_length.try_into()?) as usize;
if message_length + 6 > src.len() {
src.reserve(message_length + 6 - src.len());
return Ok(None);
}
let (message_data, _) = rest.split_at(message_length);
let mut mumble_message: Option<MumbleMessage> = None;
match message_type { match message_type {
0 => { 0 => {
//TODO: Version mumble_message = Some(MumbleMessage::Version {
let version = Version::decode(message_data); data: Version::decode(message_data)?,
println!("{version:?}"); });
Ok(Some(())) }
1 => {
mumble_message = Some(MumbleMessage::UdpTunnel {
data: UdpTunnel::decode(message_data)?,
});
}
2 => {
bail!("The server should never send Authenticate")
}
3 => {
mumble_message = Some(MumbleMessage::Ping {
data: Ping::decode(message_data)?,
});
}
4 => {
mumble_message = Some(MumbleMessage::Reject {
data: Reject::decode(message_data)?,
});
}
5 => {
mumble_message = Some(MumbleMessage::ServerSync {
data: ServerSync::decode(message_data)?,
});
}
6 => {
mumble_message = Some(MumbleMessage::ChannelRemove {
data: ChannelRemove::decode(message_data)?,
});
}
7 => {
mumble_message = Some(MumbleMessage::ChannelState {
data: ChannelState::decode(message_data)?,
});
}
8 => {
mumble_message = Some(MumbleMessage::UserRemove {
data: UserRemove::decode(message_data)?,
});
}
9 => {
mumble_message = Some(MumbleMessage::UserState {
data: UserState::decode(message_data)?,
});
}
10 => {
mumble_message = Some(MumbleMessage::BanList {
data: BanList::decode(message_data)?,
});
}
11 => {
mumble_message = Some(MumbleMessage::TextMessage {
data: TextMessage::decode(message_data)?,
});
}
12 => {
mumble_message = Some(MumbleMessage::PermissionDenied {
data: PermissionDenied::decode(message_data)?,
});
}
13 => {
mumble_message = Some(MumbleMessage::Acl {
data: Acl::decode(message_data)?,
});
}
14 => {
mumble_message = Some(MumbleMessage::QueryUsers {
data: QueryUsers::decode(message_data)?,
});
}
15 => {
mumble_message = Some(MumbleMessage::CryptSetup {
data: CryptSetup::decode(message_data)?,
});
}
16 => {
mumble_message = Some(MumbleMessage::ContextActionModify {
data: ContextActionModify::decode(message_data)?,
});
}
17 => {
mumble_message = Some(MumbleMessage::ContextAction {
data: ContextAction::decode(message_data)?,
});
}
18 => {
mumble_message = Some(MumbleMessage::UserList {
data: UserList::decode(message_data)?,
});
}
19 => {
mumble_message = Some(MumbleMessage::VoiceTarget {
data: VoiceTarget::decode(message_data)?,
});
}
20 => {
mumble_message = Some(MumbleMessage::PermissionQuery {
data: PermissionQuery::decode(message_data)?,
});
}
21 => {
mumble_message = Some(MumbleMessage::CodecVersion {
data: CodecVersion::decode(message_data)?,
});
}
22 => {
mumble_message = Some(MumbleMessage::UserStats {
data: UserStats::decode(message_data)?,
});
}
23 => {
mumble_message = Some(MumbleMessage::RequestBlob {
data: RequestBlob::decode(message_data)?,
});
}
24 => {
mumble_message = Some(MumbleMessage::ServerConfig {
data: ServerConfig::decode(message_data)?,
});
}
25 => {
mumble_message = Some(MumbleMessage::SuggestConfig {
data: SuggestConfig::decode(message_data)?,
});
} }
_ => { _ => {
eprintln!("Unknown message type {:?}", message_type); eprintln!("Unknown message type {:?}", message_type);
Ok(None)
} }
} }
src.advance(message_length + 6);
Ok(mumble_message)
}
}
impl Encoder<MumbleMessage> for MumbleTcpCodec {
type Error = anyhow::Error;
fn encode(&mut self, item: MumbleMessage, dst: &mut BytesMut) -> Result<(), Self::Error> {
let mut message = BytesMut::new();
match item {
MumbleMessage::Version { data } => {
Version::encode(&data, &mut message)?;
dst.put_u16(0);
}
MumbleMessage::Authenticate { data } => {
Authenticate::encode(&data, &mut message)?;
dst.put_u16(2);
}
MumbleMessage::Ping { data } => {
Ping::encode(&data, &mut message)?;
dst.put_u16(3);
}
_ => {
bail!("Could not encode unknown message")
}
}
dst.put_u32(message.len() as u32);
dst.extend_from_slice(&message);
Ok(())
} }
} }

View file

@ -1,4 +1,5 @@
pub mod codec; pub mod codec;
use futures::sink::SinkExt;
pub mod proto { pub mod proto {
include!(concat!(env!("OUT_DIR"), "/mumble_proto.rs")); include!(concat!(env!("OUT_DIR"), "/mumble_proto.rs"));
@ -8,45 +9,95 @@ pub mod udp {
include!(concat!(env!("OUT_DIR"), "/mumble_udp.rs")); include!(concat!(env!("OUT_DIR"), "/mumble_udp.rs"));
} }
use std::fmt::{Debug, Formatter}; use crate::codec::MumbleTcpCodec;
use std::net::SocketAddr; use crate::proto::*;
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 rustls_pki_types::{CertificateDer, ServerName, UnixTime};
use tokio_util::codec::FramedRead; use std::fmt::{Debug, Formatter};
use crate::codec::MumbleCodec; use std::net::Ipv4Addr;
use bytes::BytesMut; use std::sync::Arc;
use tokio_rustls::rustls::client::danger::{DangerousClientConfig, HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; use tokio::net::{TcpStream, UdpSocket};
use tokio_rustls::client::TlsStream;
use tokio_rustls::rustls::client::danger::{
HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier,
};
use tokio_rustls::rustls::{
ClientConfig, DigitallySignedStruct, Error, RootCertStore, SignatureScheme,
};
use tokio_rustls::TlsConnector;
use tokio_util::codec::Framed;
#[derive(Debug)]
pub enum MumbleMessage {
Version { data: Version },
UdpTunnel { data: UdpTunnel },
Ping { data: Ping },
Reject { data: Reject },
Authenticate { data: Authenticate },
ServerSync { data: ServerSync },
ChannelRemove { data: ChannelRemove },
ChannelState { data: ChannelState },
UserRemove { data: UserRemove },
UserState { data: UserState },
BanList { data: BanList },
TextMessage { data: TextMessage },
PermissionDenied { data: PermissionDenied },
Acl { data: Acl },
QueryUsers { data: QueryUsers },
CryptSetup { data: CryptSetup },
ContextActionModify { data: ContextActionModify },
ContextAction { data: ContextAction },
UserList { data: UserList },
VoiceTarget { data: VoiceTarget },
PermissionQuery { data: PermissionQuery },
CodecVersion { data: CodecVersion },
UserStats { data: UserStats },
RequestBlob { data: RequestBlob },
ServerConfig { data: ServerConfig },
SuggestConfig { data: SuggestConfig },
}
pub struct MumbleClient { pub struct MumbleClient {
host: SocketAddr, host: String,
name: String,
port: u16,
password: Option<String>,
} }
pub(crate) struct NoVerifier; pub(crate) struct NoVerifier;
impl Debug for NoVerifier { impl Debug for NoVerifier {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
todo!() write!(f, "NoVerifier")
} }
} }
impl ServerCertVerifier for NoVerifier { 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> { fn verify_server_cert(
&self,
_end_entity: &CertificateDer<'_>,
_intermediates: &[CertificateDer<'_>],
_server_name: &ServerName<'_>,
_ocsp_response: &[u8],
_now: UnixTime,
) -> Result<ServerCertVerified, Error> {
Ok(ServerCertVerified::assertion()) Ok(ServerCertVerified::assertion())
} }
fn verify_tls12_signature(&self, message: &[u8], cert: &CertificateDer<'_>, dss: &DigitallySignedStruct) -> Result<HandshakeSignatureValid, Error> { fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &CertificateDer<'_>,
_dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
Ok(HandshakeSignatureValid::assertion()) Ok(HandshakeSignatureValid::assertion())
} }
fn verify_tls13_signature(&self, message: &[u8], cert: &CertificateDer<'_>, dss: &DigitallySignedStruct) -> Result<HandshakeSignatureValid, Error> { fn verify_tls13_signature(
&self,
_message: &[u8],
_cert: &CertificateDer<'_>,
_dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, Error> {
Ok(HandshakeSignatureValid::assertion()) Ok(HandshakeSignatureValid::assertion())
} }
@ -70,9 +121,12 @@ impl ServerCertVerifier for NoVerifier {
} }
impl MumbleClient { impl MumbleClient {
pub fn new(host: SocketAddr) -> Self { pub fn new(host: String, port: Option<u16>, name: String, password: Option<String>) -> Self {
Self { Self {
host host,
port: port.unwrap_or(64738),
name,
password,
} }
} }
@ -89,37 +143,48 @@ impl MumbleClient {
.set_certificate_verifier(Arc::new(NoVerifier)); .set_certificate_verifier(Arc::new(NoVerifier));
let connector = TlsConnector::from(Arc::new(config)); let connector = TlsConnector::from(Arc::new(config));
let dnsname = ServerName::try_from("127.0.0.1")?; let dnsname = ServerName::try_from(self.host.clone())?;
let stream = TcpStream::connect(&self.host).await?; let stream = TcpStream::connect((self.host.clone(), self.port)).await?;
Ok(connector.connect(dnsname, stream).await?) Ok(connector.connect(dnsname, stream).await?)
} }
pub async fn connect(&mut self) -> anyhow::Result<()> { pub async fn create_udp_connection(&mut self) -> anyhow::Result<UdpSocket> {
let tcp_stream = self.create_tcp_connection().await?; let sock = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 0)).await?;
let mut framed_reader = FramedRead::new(tcp_stream, MumbleCodec::new()); sock.connect((self.host.clone(), self.port)).await?;
let mut message_buffer: BytesMut = BytesMut::new(); Ok(sock)
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(); pub async fn connect(
// complete_buffer.extend((0u16).to_be_bytes()); &mut self,
// complete_buffer.extend(((2+4+message_buffer.len()) as u32).to_be_bytes()); ) -> anyhow::Result<Framed<TlsStream<TcpStream>, MumbleTcpCodec>> {
// complete_buffer.extend(&message_buffer); let mut framed = Framed::new(self.create_tcp_connection().await?, MumbleTcpCodec::new());
let version_v1 = (1u16 as u32) << 16 | (5u8 as u32) << 8 | (1u8 as u32);
let version_v2 = 1u64 << 48 | 5u64 << 32 | 0u64 << 16 | 1u64;
framed
.send(MumbleMessage::Version {
data: Version {
os: Some(String::from("Linux")),
os_version: Some(String::from("os version")),
release: Some(String::from("release")),
version_v1: Some(version_v1),
version_v2: Some(version_v2),
},
})
.await?;
// println!("{complete_buffer:?}"); framed
// tcp_stream.write_all(&complete_buffer).await?; .send(MumbleMessage::Authenticate {
data: Authenticate {
username: Some(self.name.clone()),
password: self.password.clone(),
tokens: vec![],
celt_versions: vec![],
opus: Some(true),
client_type: Some(1),
},
})
.await?;
while let Some(frame_result) = framed_reader.next().await { Ok(framed)
if let Ok(data) = frame_result {
println!("{data:?}");
}
}
Ok(())
} }
} }

View file

@ -1,8 +0,0 @@
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(())
}