Compare commits
6 Commits
206bf23bdf
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| e1f3bca708 | |||
| 105deab45d | |||
| 4055bf24ab | |||
| 95c57c4850 | |||
| b19f629605 | |||
| 1d8f3fd791 |
@@ -1,3 +1,5 @@
|
||||
/target
|
||||
cert.pem
|
||||
key.pem
|
||||
bundle
|
||||
config.toml
|
||||
|
||||
Generated
+1040
-248
File diff suppressed because it is too large
Load Diff
@@ -7,9 +7,17 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.86"
|
||||
axum = "0.7.7"
|
||||
axum-server = { version = "0.7.1", features = ["tls-rustls"] }
|
||||
lazy_static = "1.4.0"
|
||||
rustls-pemfile = "2.2.0"
|
||||
serde = { version = "1.0.214", features = ["derive"] }
|
||||
serde_json = "1.0.132"
|
||||
tokio = { version = "1.37.0", features = ["full"] }
|
||||
tokio-rustls = "0.26.0"
|
||||
toml = "0.8.19"
|
||||
tower = "0.5.1"
|
||||
tower-http = { version = "0.6.1", features = ["fs"] }
|
||||
tracing = { version = "0.1.40", features = ["async-await"] }
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
wtransport = "0.1.13"
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
|
||||
cargo build --release
|
||||
|
||||
rm -rf mumble-web2
|
||||
git clone https://git.ohea.xyz/mumble/mumble-web2
|
||||
cd mumble-web2
|
||||
echo "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" >server_hash.txt
|
||||
dx build --release
|
||||
cd ..
|
||||
|
||||
rm -rf bundle
|
||||
mkdir bundle
|
||||
cp target/release/mumble-webtransport-proxy bundle
|
||||
cp -r mumble-web2/dist bundle/gui
|
||||
@@ -0,0 +1,11 @@
|
||||
proxy_listen_address = "127.0.0.1:4433"
|
||||
http_listen_address = "127.0.0.1:4434"
|
||||
cert_path = "./cert.pem"
|
||||
key_path = "./key.pem"
|
||||
mumble_server_url = "voip.ohea.xyz:64738"
|
||||
gui_path = "../mumble-web2/dist"
|
||||
serve_https = true
|
||||
|
||||
[gui]
|
||||
proxy_url = "https://voip2.ohea.xyz"
|
||||
# cert_hash = [...]
|
||||
+150
-23
@@ -1,6 +1,13 @@
|
||||
use anyhow::Result;
|
||||
use std::net::ToSocketAddrs;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use axum::extract::State;
|
||||
use axum::http::{Response, StatusCode};
|
||||
use axum::response::IntoResponse;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::future::IntoFuture;
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use tokio::fs::read_to_string;
|
||||
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::pin;
|
||||
@@ -81,13 +88,133 @@ impl ServerCertVerifier for NoCertificateVerification {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
struct GuiConfig {
|
||||
proxy_url: Option<String>,
|
||||
cert_hash: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Deserialize)]
|
||||
struct Config {
|
||||
proxy_listen_address: SocketAddr,
|
||||
http_listen_address: SocketAddr,
|
||||
cert_path: String,
|
||||
key_path: String,
|
||||
#[serde(default)]
|
||||
serve_https: bool,
|
||||
mumble_server_url: String,
|
||||
gui_path: PathBuf,
|
||||
gui: GuiConfig,
|
||||
}
|
||||
|
||||
//async fn serve_index_html_with_config(State(config): State<Config>) -> impl IntoResponse {
|
||||
async fn serve_index_html_with_config(State(config): State<Config>) -> impl IntoResponse {
|
||||
// Load the HTML file
|
||||
let html = match read_to_string(config.gui_path.join("index.html")).await {
|
||||
Ok(content) => content,
|
||||
Err(_) => return (StatusCode::NOT_FOUND, "File not found").into_response(),
|
||||
};
|
||||
|
||||
// Insert the script tag with configuration
|
||||
let modified_html = html.replace(
|
||||
"</head>",
|
||||
&format!(
|
||||
"<script>window.config = {}</script>\n</head>",
|
||||
serde_json::to_string(&config.gui).unwrap(),
|
||||
),
|
||||
);
|
||||
|
||||
// Create a response with the modified HTML
|
||||
Response::builder()
|
||||
.status(StatusCode::OK)
|
||||
.header("Content-Type", "text/html")
|
||||
.body(modified_html)
|
||||
.unwrap()
|
||||
.into_response()
|
||||
}
|
||||
|
||||
fn configure_tls(config: &Config) -> Result<rustls::ServerConfig, anyhow::Error> {
|
||||
// Thanks perplexity!
|
||||
use rustls_pemfile::{certs, pkcs8_private_keys};
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
|
||||
// Create a new ServerConfig with no client authentication
|
||||
//(rustls::server::NoClientAuth::new());
|
||||
|
||||
// Read the certificate file
|
||||
let cert_file = File::open(&config.cert_path)?;
|
||||
let mut cert_reader = BufReader::new(cert_file);
|
||||
let cert_chain = certs(&mut cert_reader).collect::<Result<_, _>>()?;
|
||||
|
||||
// Read the private key file
|
||||
let key_file = File::open(&config.key_path)?;
|
||||
let mut key_reader = BufReader::new(key_file);
|
||||
let key = pkcs8_private_keys(&mut key_reader)
|
||||
.next()
|
||||
.ok_or(anyhow!("no keys in key.pem"))??;
|
||||
|
||||
// Set the certificate chain and private key
|
||||
let config = rustls::ServerConfig::builder()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(cert_chain, key.into())?;
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
init_logging();
|
||||
|
||||
let proxy_config: Config = toml::from_str(
|
||||
&read_to_string("./config.toml")
|
||||
.await
|
||||
.context("reading config.toml (try making a copy of config.toml.example)")?,
|
||||
)?;
|
||||
|
||||
let mumble_server_addr = proxy_config
|
||||
.mumble_server_url
|
||||
.to_socket_addrs()
|
||||
.context(format!(
|
||||
"parsing mumble_server_url={}",
|
||||
proxy_config.mumble_server_url
|
||||
))?
|
||||
.next()
|
||||
.ok_or(anyhow!(
|
||||
"no socket addrs in mumble_server_url={}",
|
||||
proxy_config.mumble_server_url
|
||||
))?;
|
||||
|
||||
// Setup HTTP Server
|
||||
//let http = axum::Router::new().route("/", axum::routing::get(serve_gui));
|
||||
let app = axum::Router::new()
|
||||
.route("/", axum::routing::get(serve_index_html_with_config))
|
||||
.fallback_service(tower_http::services::ServeDir::new(&proxy_config.gui_path))
|
||||
.with_state(proxy_config.clone());
|
||||
if proxy_config.serve_https {
|
||||
tokio::spawn(
|
||||
axum_server::bind_rustls(
|
||||
proxy_config.http_listen_address,
|
||||
axum_server::tls_rustls::RustlsConfig::from_config(Arc::new(configure_tls(
|
||||
&proxy_config,
|
||||
)?)),
|
||||
)
|
||||
.serve(app.into_make_service())
|
||||
.into_future(),
|
||||
);
|
||||
} else {
|
||||
tokio::spawn(
|
||||
axum_server::bind(proxy_config.http_listen_address)
|
||||
.serve(app.into_make_service())
|
||||
.into_future(),
|
||||
);
|
||||
}
|
||||
|
||||
// Setup WebTransport proxy listener
|
||||
let identity = Identity::load_pemfiles(proxy_config.cert_path, proxy_config.key_path).await?;
|
||||
let config = ServerConfig::builder()
|
||||
.with_bind_default(4433)
|
||||
.with_identity(&Identity::load_pemfiles("cert.pem", "key.pem").await?)
|
||||
.with_bind_address(proxy_config.proxy_listen_address)
|
||||
.with_identity(&identity)
|
||||
.keep_alive_interval(Some(Duration::from_secs(20)))
|
||||
.build();
|
||||
|
||||
@@ -98,21 +225,30 @@ async fn main() -> Result<()> {
|
||||
for id in 0.. {
|
||||
let incoming_session = server.accept().await;
|
||||
tokio::spawn(
|
||||
handle_connection(incoming_session, id).instrument(info_span!("Connection", id)),
|
||||
handle_connection(incoming_session, id, mumble_server_addr)
|
||||
.instrument(info_span!("Connection", id)),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_connection(incoming_session: IncomingSession, id: usize) {
|
||||
async fn handle_connection(
|
||||
incoming_session: IncomingSession,
|
||||
id: usize,
|
||||
mumble_server_address: SocketAddr,
|
||||
) {
|
||||
// Wrapper to handle connection establishment failures
|
||||
if let Err(e) = handle_connection_impl(incoming_session, id).await {
|
||||
if let Err(e) = handle_connection_impl(incoming_session, id, mumble_server_address).await {
|
||||
error!("{:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_connection_impl(incoming_session: IncomingSession, id: usize) -> Result<()> {
|
||||
async fn handle_connection_impl(
|
||||
incoming_session: IncomingSession,
|
||||
id: usize,
|
||||
mumble_server_address: SocketAddr,
|
||||
) -> Result<()> {
|
||||
info!("Waiting for session request...");
|
||||
|
||||
let session_request = incoming_session.await?;
|
||||
@@ -135,17 +271,9 @@ async fn handle_connection_impl(incoming_session: IncomingSession, id: usize) ->
|
||||
|
||||
let connector = TlsConnector::from(Arc::new(config));
|
||||
|
||||
//let addr = "127.0.0.1:64738"
|
||||
let addr = "ohea.xyz:64738"
|
||||
.to_string()
|
||||
.to_socket_addrs()?
|
||||
.next()
|
||||
.unwrap();
|
||||
|
||||
let server_tcp = TcpStream::connect(addr).await?;
|
||||
let server_tcp = TcpStream::connect(mumble_server_address).await?;
|
||||
let server_stream = connector
|
||||
//.connect("127.0.0.1".try_into()?, server_tcp)
|
||||
.connect("ohea.xyz".try_into()?, server_tcp)
|
||||
.connect("example.com".try_into()?, server_tcp)
|
||||
.await?;
|
||||
let (read_server, write_server) = tokio::io::split(server_stream);
|
||||
|
||||
@@ -185,13 +313,13 @@ async fn client_to_server_loop(
|
||||
let mut buffer = vec![0; 65536].into_boxed_slice();
|
||||
pin!(server_stream);
|
||||
loop {
|
||||
info!("Reading Data");
|
||||
let bytes_read = match client_stream.read(&mut buffer).await? {
|
||||
Some(bytes_read) => bytes_read,
|
||||
None => break Ok(()),
|
||||
};
|
||||
info!("Writing data");
|
||||
server_stream.write(&buffer[..bytes_read]).await?;
|
||||
|
||||
server_stream.write_all(&buffer[..bytes_read]).await?;
|
||||
server_stream.flush().await?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,9 +338,8 @@ async fn server_to_client_loop(
|
||||
let mut buffer = vec![0; 65536].into_boxed_slice();
|
||||
pin!(server_stream);
|
||||
loop {
|
||||
info!("Reading Data from server");
|
||||
let bytes_read = server_stream.read(&mut buffer).await?;
|
||||
info!("Writing data to client");
|
||||
|
||||
client_stream.write_all(&buffer[..bytes_read]).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user