Initial commit...it works?
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
/target
|
||||||
|
certificate.key
|
||||||
|
certificate.pem
|
||||||
Generated
+1714
File diff suppressed because it is too large
Load Diff
+20
@@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "mumble-web2-proxy"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.79"
|
||||||
|
futures = "0.3.30"
|
||||||
|
#mumble-protocol = "0.4.1"
|
||||||
|
mumble-protocol-2x = { path = "/home/robby/working/rust-mumble-protocol" }
|
||||||
|
native-tls = "0.2.11"
|
||||||
|
tokio = { version = "1.35.1", features = ["full"] }
|
||||||
|
tokio-native-tls = "0.3.1"
|
||||||
|
tokio-util = { version = "0.7.0", features = ["codec", "net"]}
|
||||||
|
tracing = "0.1.40"
|
||||||
|
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||||
|
url = "2.5.0"
|
||||||
|
wtransport = "0.1.10"
|
||||||
+251
@@ -0,0 +1,251 @@
|
|||||||
|
use anyhow::anyhow;
|
||||||
|
use anyhow::Result;
|
||||||
|
use futures::SinkExt;
|
||||||
|
use futures::StreamExt;
|
||||||
|
use mumble_protocol_2x::control::msgs;
|
||||||
|
use mumble_protocol_2x::control::ClientControlCodec;
|
||||||
|
use mumble_protocol_2x::control::ControlPacket;
|
||||||
|
use mumble_protocol_2x::crypt::ClientCryptState;
|
||||||
|
use std::net::ToSocketAddrs;
|
||||||
|
use std::time::Duration;
|
||||||
|
use tokio::net::TcpStream;
|
||||||
|
use tokio_native_tls::TlsConnector;
|
||||||
|
use tokio_util::codec::Decoder;
|
||||||
|
use tracing::error;
|
||||||
|
use tracing::info;
|
||||||
|
use tracing::info_span;
|
||||||
|
use tracing::Instrument;
|
||||||
|
use tracing_subscriber::filter::LevelFilter;
|
||||||
|
use tracing_subscriber::EnvFilter;
|
||||||
|
use wtransport::endpoint::IncomingSession;
|
||||||
|
use wtransport::Certificate;
|
||||||
|
use wtransport::Endpoint;
|
||||||
|
use wtransport::ServerConfig;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<()> {
|
||||||
|
init_logging();
|
||||||
|
|
||||||
|
let certificate = Certificate::load("./certificate.pem", "./certificate.key").await?;
|
||||||
|
let hashes = certificate.hashes();
|
||||||
|
info!("{}", hashes[0].fmt_as_byte_array());
|
||||||
|
|
||||||
|
let config = ServerConfig::builder()
|
||||||
|
.with_bind_default(4433)
|
||||||
|
//.with_certificate(Certificate::self_signed(["localhost"]))
|
||||||
|
.with_certificate(certificate)
|
||||||
|
.keep_alive_interval(Some(Duration::from_secs(3)))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let server = Endpoint::server(config)?;
|
||||||
|
info!("Server ready!");
|
||||||
|
|
||||||
|
for id in 0.. {
|
||||||
|
let incoming_session = server.accept().await;
|
||||||
|
tokio::spawn(handle_connection(incoming_session).instrument(info_span!("Connection", id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_connection(incoming_session: IncomingSession) {
|
||||||
|
let result = handle_connection_impl(incoming_session).await;
|
||||||
|
error!("{:?}", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_connection_impl(incoming_session: IncomingSession) -> Result<()> {
|
||||||
|
let mut buffer = vec![0; 65536].into_boxed_slice();
|
||||||
|
|
||||||
|
info!("Waiting for session request...");
|
||||||
|
|
||||||
|
let session_request = incoming_session.await?;
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"New session: Authority: '{}', Path: '{}'",
|
||||||
|
session_request.authority(),
|
||||||
|
session_request.path()
|
||||||
|
);
|
||||||
|
|
||||||
|
let connection_params =
|
||||||
|
connection_parameters_from_url(&session_request.authority(), &session_request.path())?;
|
||||||
|
|
||||||
|
let server_addr = (
|
||||||
|
connection_params.hostname.clone(),
|
||||||
|
connection_params.port.clone(),
|
||||||
|
)
|
||||||
|
.to_socket_addrs()
|
||||||
|
.expect("Failed to parse server address")
|
||||||
|
.next()
|
||||||
|
.expect("Failed to resolve server address");
|
||||||
|
|
||||||
|
let stream = TcpStream::connect(&server_addr)
|
||||||
|
.await
|
||||||
|
.expect("Failed to connect to server:");
|
||||||
|
|
||||||
|
let mut builder = native_tls::TlsConnector::builder();
|
||||||
|
builder.danger_accept_invalid_certs(true);
|
||||||
|
let connector: TlsConnector = builder
|
||||||
|
.build()
|
||||||
|
.expect("Failed to create TLS connector")
|
||||||
|
.into();
|
||||||
|
let tls_stream = connector
|
||||||
|
.connect(&connection_params.hostname, stream)
|
||||||
|
.await
|
||||||
|
.expect("Failed to connect TLS: {}");
|
||||||
|
|
||||||
|
let (mut sink, mut stream) = ClientControlCodec::new().framed(tls_stream).split();
|
||||||
|
|
||||||
|
let mut msg = msgs::Authenticate::new();
|
||||||
|
msg.set_username(connection_params.username);
|
||||||
|
msg.set_opus(true);
|
||||||
|
sink.send(msg.into()).await.unwrap();
|
||||||
|
|
||||||
|
let connection = session_request.accept().await?;
|
||||||
|
|
||||||
|
info!("Waiting for data from client...");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
tokio::select! {
|
||||||
|
stream = connection.accept_bi() => {
|
||||||
|
let mut stream = stream?;
|
||||||
|
info!("Accepted BI stream");
|
||||||
|
|
||||||
|
let bytes_read = match stream.1.read(&mut buffer).await? {
|
||||||
|
Some(bytes_read) => bytes_read,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
let str_data = std::str::from_utf8(&buffer[..bytes_read])?;
|
||||||
|
|
||||||
|
info!("Received (bi) '{str_data}' from client");
|
||||||
|
|
||||||
|
stream.0.write_all(b"ACK").await?;
|
||||||
|
}
|
||||||
|
stream = connection.accept_uni() => {
|
||||||
|
let mut stream = stream?;
|
||||||
|
info!("Accepted UNI stream");
|
||||||
|
|
||||||
|
let bytes_read = match stream.read(&mut buffer).await? {
|
||||||
|
Some(bytes_read) => bytes_read,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
let str_data = std::str::from_utf8(&buffer[..bytes_read])?;
|
||||||
|
|
||||||
|
info!("Received (uni) '{str_data}' from client");
|
||||||
|
|
||||||
|
let mut stream = connection.open_uni().await?.await?;
|
||||||
|
stream.write_all(b"ACK").await?;
|
||||||
|
}
|
||||||
|
dgram = connection.receive_datagram() => {
|
||||||
|
let dgram = dgram?;
|
||||||
|
let str_data = std::str::from_utf8(&dgram)?;
|
||||||
|
|
||||||
|
info!("Received (dgram) '{str_data}' from client");
|
||||||
|
|
||||||
|
connection.send_datagram(b"ACK")?;
|
||||||
|
}
|
||||||
|
Some(mumble_server_pkt) = stream.next() => {
|
||||||
|
match mumble_server_pkt.unwrap() {
|
||||||
|
ControlPacket::TextMessage(mut msg) => {
|
||||||
|
info!(
|
||||||
|
"Got message from user with session ID {}: {}",
|
||||||
|
msg.get_actor(),
|
||||||
|
msg.get_message()
|
||||||
|
);
|
||||||
|
// Send reply back to server
|
||||||
|
let mut response = msgs::TextMessage::new();
|
||||||
|
response.mut_session().push(msg.get_actor());
|
||||||
|
response.set_message(msg.take_message());
|
||||||
|
sink.send(response.into()).await.unwrap();
|
||||||
|
}
|
||||||
|
ControlPacket::CryptSetup(msg) => {
|
||||||
|
info!("Got crypt state");
|
||||||
|
//// Wait until we're fully connected before initiating UDP voice
|
||||||
|
//crypt_state = Some(ClientCryptState::new_from(
|
||||||
|
// msg.get_key()
|
||||||
|
// .try_into()
|
||||||
|
// .expect("Server sent private key with incorrect size"),
|
||||||
|
// msg.get_client_nonce()
|
||||||
|
// .try_into()
|
||||||
|
// .expect("Server sent client_nonce with incorrect size"),
|
||||||
|
// msg.get_server_nonce()
|
||||||
|
// .try_into()
|
||||||
|
// .expect("Server sent server_nonce with incorrect size"),
|
||||||
|
//));
|
||||||
|
}
|
||||||
|
ControlPacket::ServerSync(_) => {
|
||||||
|
println!("Logged in!");
|
||||||
|
//if let Some(sender) = crypt_state_sender.take() {
|
||||||
|
// let _ = sender.send(
|
||||||
|
// crypt_state
|
||||||
|
// .take()
|
||||||
|
// .expect("Server didn't send us any CryptSetup packet!"),
|
||||||
|
// );
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
ControlPacket::Reject(msg) => {
|
||||||
|
println!("Login rejected: {:?}", msg);
|
||||||
|
}
|
||||||
|
_ => {},
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_logging() {
|
||||||
|
let env_filter = EnvFilter::builder()
|
||||||
|
.with_default_directive(LevelFilter::INFO.into())
|
||||||
|
.from_env_lossy();
|
||||||
|
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_target(true)
|
||||||
|
.with_level(true)
|
||||||
|
.with_env_filter(env_filter)
|
||||||
|
.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ConnectionParameters {
|
||||||
|
pub hostname: String,
|
||||||
|
pub port: u16,
|
||||||
|
pub username: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn connection_parameters_from_url(authority: &str, path: &str) -> Result<ConnectionParameters> {
|
||||||
|
let url_str = format!("https://{}{}", authority, path);
|
||||||
|
|
||||||
|
let connection_url = url::Url::parse(&url_str)?;
|
||||||
|
|
||||||
|
let mut hostname: Option<String> = None;
|
||||||
|
let mut port: Option<u16> = None;
|
||||||
|
let mut username: Option<String> = None;
|
||||||
|
|
||||||
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
|
for (k, v) in connection_url.query_pairs() {
|
||||||
|
match k.borrow() {
|
||||||
|
"hostname" => {
|
||||||
|
hostname = Some(v.to_string());
|
||||||
|
}
|
||||||
|
"port" => {
|
||||||
|
port = Some(v.parse::<u16>()?);
|
||||||
|
}
|
||||||
|
"username" => {
|
||||||
|
username = Some(v.to_string());
|
||||||
|
}
|
||||||
|
&_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hostname.is_none() && port.is_none() && username.is_none() {
|
||||||
|
return Err(anyhow!("URL is missing required parametetrs"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ConnectionParameters {
|
||||||
|
hostname: hostname.unwrap(),
|
||||||
|
port: port.unwrap(),
|
||||||
|
username: username.unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user