Initial commit...it works?

This commit is contained in:
2024-01-23 18:01:10 -07:00
commit 94f04f64f1
4 changed files with 1988 additions and 0 deletions
+251
View File
@@ -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(),
})
}