[WIP] Webtransport

This commit is contained in:
Alex Emmet 2026-03-03 22:13:54 +01:00
commit eed2674e44
3 changed files with 336 additions and 98 deletions

279
Cargo.lock generated
View file

@ -148,6 +148,45 @@ version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "asn1-rs"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60"
dependencies = [
"asn1-rs-derive",
"asn1-rs-impl",
"displaydoc",
"nom",
"num-traits",
"rusticata-macros",
"thiserror 2.0.18",
"time",
]
[[package]]
name = "asn1-rs-derive"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "asn1-rs-impl"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "async-channel"
version = "1.9.0"
@ -361,6 +400,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf"
dependencies = [
"aws-lc-sys",
"untrusted 0.7.1",
"zeroize",
]
@ -933,6 +973,20 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b"
[[package]]
name = "der-parser"
version = "10.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6"
dependencies = [
"asn1-rs",
"displaydoc",
"nom",
"num-bigint",
"num-traits",
"rusticata-macros",
]
[[package]]
name = "deranged"
version = "0.5.8"
@ -1036,7 +1090,7 @@ dependencies = [
[[package]]
name = "epsilon-core"
version = "0.1.0"
source = "git+https://github.com/Tensamin/Epsilon.git#b750d4756813367cd499ba44c22cf52830afe5b3"
source = "git+https://github.com/Tensamin/Epsilon.git#4bdd32b44a77ac980cede20a1d401004f52e4a29"
dependencies = [
"byteorder",
"quinn",
@ -1048,15 +1102,19 @@ dependencies = [
[[package]]
name = "epsilon-native"
version = "0.1.0"
source = "git+https://github.com/Tensamin/Epsilon.git#b750d4756813367cd499ba44c22cf52830afe5b3"
source = "git+https://github.com/Tensamin/Epsilon.git#4bdd32b44a77ac980cede20a1d401004f52e4a29"
dependencies = [
"anyhow",
"async-trait",
"aws-lc-rs",
"bytes 1.11.1",
"epsilon-core",
"quinn",
"rcgen",
"rustls 0.23.37",
"thiserror 2.0.18",
"tokio 1.50.0",
"wtransport",
]
[[package]]
@ -1354,20 +1412,20 @@ dependencies = [
"cfg-if",
"js-sys",
"libc",
"r-efi",
"r-efi 5.3.0",
"wasip2",
"wasm-bindgen",
]
[[package]]
name = "getrandom"
version = "0.4.1"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec"
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"r-efi 6.0.0",
"wasip2",
"wasip3",
]
@ -1591,6 +1649,12 @@ dependencies = [
"digest",
]
[[package]]
name = "httlib-huffman"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a9fcbcc408c5526c3ab80d534e5c86e7967c1fb7aa0a8c76abd1edc27deb877"
[[package]]
name = "http"
version = "0.2.12"
@ -1720,7 +1784,7 @@ dependencies = [
"libc",
"percent-encoding",
"pin-project-lite 0.2.17",
"socket2",
"socket2 0.6.2",
"system-configuration",
"tokio 1.50.0",
"tower-service",
@ -2235,6 +2299,12 @@ dependencies = [
"unicase",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
@ -2280,6 +2350,16 @@ dependencies = [
"tempfile",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "ntapi"
version = "0.4.3"
@ -2298,12 +2378,31 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-conv"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
@ -2313,6 +2412,21 @@ dependencies = [
"autocfg",
]
[[package]]
name = "octets"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8311fa8ab7a57759b4ff1f851a3048d9ef0effaa0130726426b742d26d8a88e7"
[[package]]
name = "oid-registry"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7"
dependencies = [
"asn1-rs",
]
[[package]]
name = "once_cell"
version = "1.21.3"
@ -2458,6 +2572,16 @@ dependencies = [
"sha2",
]
[[package]]
name = "pem"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be"
dependencies = [
"base64 0.22.1",
"serde_core",
]
[[package]]
name = "percent-encoding"
version = "2.3.2"
@ -2683,7 +2807,7 @@ dependencies = [
"quinn-udp",
"rustc-hash",
"rustls 0.23.37",
"socket2",
"socket2 0.6.2",
"thiserror 2.0.18",
"tokio 1.50.0",
"tracing",
@ -2723,7 +2847,7 @@ dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2",
"socket2 0.6.2",
"tracing",
"windows-sys 0.60.2",
]
@ -2743,6 +2867,12 @@ version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rand"
version = "0.8.5"
@ -2828,6 +2958,20 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "rcgen"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10b99e0098aa4082912d4c649628623db6aba77335e4f4569ff5083a6448b32e"
dependencies = [
"pem",
"ring 0.17.14",
"rustls-pki-types",
"time",
"x509-parser 0.18.1",
"yasna",
]
[[package]]
name = "redox_syscall"
version = "0.5.18"
@ -2991,6 +3135,15 @@ dependencies = [
"semver",
]
[[package]]
name = "rusticata-macros"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
dependencies = [
"nom",
]
[[package]]
name = "rustix"
version = "1.1.4"
@ -3398,6 +3551,16 @@ version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "socket2"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "socket2"
version = "0.6.2"
@ -3581,7 +3744,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0"
dependencies = [
"fastrand",
"getrandom 0.4.1",
"getrandom 0.4.2",
"once_cell",
"rustix",
"windows-sys 0.61.2",
@ -3652,10 +3815,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [
"deranged",
"itoa",
"num-conv",
"powerfmt",
"serde_core",
"time-core",
"time-macros",
]
[[package]]
@ -3664,6 +3829,16 @@ version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]]
name = "time-macros"
version = "0.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
dependencies = [
"num-conv",
"time-core",
]
[[package]]
name = "tinystr"
version = "0.8.2"
@ -3711,7 +3886,7 @@ dependencies = [
"parking_lot",
"pin-project-lite 0.2.17",
"signal-hook-registry",
"socket2",
"socket2 0.6.2",
"tokio-macros",
"windows-sys 0.61.2",
]
@ -4172,7 +4347,7 @@ version = "1.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb"
dependencies = [
"getrandom 0.4.1",
"getrandom 0.4.2",
"js-sys",
"serde_core",
"wasm-bindgen",
@ -4913,6 +5088,42 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
[[package]]
name = "wtransport"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e56b611f195638f3790e4e5a41e9d777643a6c324ae4ffd1f0f53f2738e7678c"
dependencies = [
"bytes 1.11.1",
"pem",
"quinn",
"rcgen",
"rustls 0.23.37",
"rustls-native-certs",
"rustls-pki-types",
"sha2",
"socket2 0.5.10",
"thiserror 2.0.18",
"time",
"tokio 1.50.0",
"tracing",
"url",
"wtransport-proto",
"x509-parser 0.17.0",
]
[[package]]
name = "wtransport-proto"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1627c5b59450278e9771aab35275d72bfa2788128c197c5af1e4a820c8737ef4"
dependencies = [
"httlib-huffman",
"octets",
"thiserror 2.0.18",
"url",
]
[[package]]
name = "x448"
version = "0.6.0"
@ -4934,6 +5145,50 @@ dependencies = [
"cookie-factory",
]
[[package]]
name = "x509-parser"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460"
dependencies = [
"asn1-rs",
"data-encoding",
"der-parser",
"lazy_static",
"nom",
"oid-registry",
"rusticata-macros",
"thiserror 2.0.18",
"time",
]
[[package]]
name = "x509-parser"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202"
dependencies = [
"asn1-rs",
"data-encoding",
"der-parser",
"lazy_static",
"nom",
"oid-registry",
"ring 0.17.14",
"rusticata-macros",
"thiserror 2.0.18",
"time",
]
[[package]]
name = "yasna"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd"
dependencies = [
"time",
]
[[package]]
name = "yoke"
version = "0.8.1"

View file

@ -9,6 +9,7 @@ use std::env;
use dotenv::dotenv;
use once_cell::sync::Lazy;
use rustls::crypto::aws_lc_rs;
use crate::{
calls::call_util::garbage_collect_calls,
@ -31,6 +32,7 @@ pub fn get_public_key() -> x448::PublicKey {
#[tokio::main]
async fn main() {
aws_lc_rs::default_provider().install_default();
dotenv().ok();
startup();

View file

@ -34,8 +34,8 @@ use uuid::Uuid;
// Configuration
// ============================================================================
const OMEGA_HOST_DEFAULT: &str = "omega.tensamin.net";
const OMEGA_PORT_DEFAULT: u16 = 443;
const OMEGA_HOST_DEFAULT: &str = "188.114.97.0";
const OMEGA_PORT_DEFAULT: u16 = 9187;
const RECONNECT_DELAY: Duration = Duration::from_secs(5);
const MAX_RECONNECT_DELAY: Duration = Duration::from_secs(300);
const CONNECTION_TIMEOUT: Duration = Duration::from_secs(10);
@ -213,16 +213,21 @@ impl OmegaConnection {
let addr_str = format!("{}:{}", self.host, self.port);
// Resolve address (DNS lookup)
let remote_addr = tokio::net::lookup_host(&addr_str)
let addrs: Vec<SocketAddr> = tokio::net::lookup_host(&addr_str)
.await
.map_err(|e| format!("DNS lookup failed for {}: {}", addr_str, e))?
.next()
.ok_or_else(|| format!("No addresses found for {}", addr_str))?;
.collect();
let bind_addr: SocketAddr = "0.0.0.0:0".parse().unwrap();
let endpoint =
Endpoint::client(bind_addr).map_err(|e| format!("Failed to create endpoint: {}", e))?;
if addrs.is_empty() {
return Err(format!("No addresses found for {}", addr_str));
}
// Prefer IPv4 if available, otherwise use first available (IPv6 or IPv4)
let remote_addr = addrs
.iter()
.find(|a| a.is_ipv4())
.copied()
.unwrap_or_else(|| addrs[0]);
log_in!(
0,
@ -232,24 +237,19 @@ impl OmegaConnection {
remote_addr
);
// Connect to Omega server
let connection = endpoint
.connect(remote_addr, &self.host)
.map_err(|e| format!("Connect failed: {}", e))?
// Connect using epsilon_native wrapper
let (sender, receiver) = epsilon_native::client::connect(remote_addr)
.await
.map_err(|e| format!("Connection failed: {}", e))?;
log_in!(
0,
PrintType::Omega,
"QUIC connection established to {}",
addr_str
"QUIC connection established to {} (via {})",
addr_str,
remote_addr
);
// Create Sender and Receiver using your epsilon_native API
let sender = Sender::new(connection.clone());
let receiver = Receiver::new(connection);
// Store sender
*self.sender.write().await = Some(Arc::new(sender));
*self.state.write().await = ConnectionState::Connected { identified: false };
@ -287,21 +287,6 @@ impl OmegaConnection {
}
}
fn load_client_tls(&self) -> Result<RustlsClientConfig, Box<dyn std::error::Error>> {
let _ = aws_lc_rs::default_provider().install_default();
let mut root_store = rustls::RootCertStore::empty();
// Add webpki roots for system CA certificates
root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
let config = RustlsClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth();
Ok(config)
}
// -------------------------------------------------------------------------
// Identification Handshake
// -------------------------------------------------------------------------
@ -601,53 +586,26 @@ impl OmegaConnection {
pub async fn is_identified(&self) -> bool {
self.state.read().await.is_identified()
}
}
// ============================================================================
// Global Instance
// ============================================================================
static OMEGA_CONNECTION: Lazy<Arc<OmegaConnection>> = Lazy::new(|| {
let conn = Arc::new(OmegaConnection::new());
// Start the connection manager immediately
let conn_clone = conn.clone();
tokio::spawn(async move {
conn_clone.start().await;
});
start_task_cleanup_loop();
conn
});
pub fn get_omega_connection() -> Arc<OmegaConnection> {
OMEGA_CONNECTION.clone()
}
// ============================================================================
// Global Helpers
// ============================================================================
pub async fn close_iota(iota_id: i64) {
pub async fn close_iota(iota_id: i64) {
let cv = CommunicationValue::new(CommunicationType::iota_disconnected)
.add_data(DataTypes::iota_id, DataValue::Number(iota_id));
OMEGA_CONNECTION.send_message(&cv).await;
}
}
pub async fn client_changed(_iota_id: i64, user_id: i64, state: UserStatus) {
pub async fn client_changed(_iota_id: i64, user_id: i64, state: UserStatus) {
let msg_type = match state {
UserStatus::iota_offline => CommunicationType::user_disconnected,
UserStatus::user_offline => CommunicationType::user_disconnected,
_ => CommunicationType::user_connected,
};
let cv =
CommunicationValue::new(msg_type).add_data(DataTypes::user_id, DataValue::Number(user_id));
let cv = CommunicationValue::new(msg_type)
.add_data(DataTypes::user_id, DataValue::Number(user_id));
OMEGA_CONNECTION.send_message(&cv).await;
}
}
pub async fn user_states(user_id: i64, user_ids: Vec<i64>) {
pub async fn user_states(user_id: i64, user_ids: Vec<i64>) {
let user_ids = user_ids.iter().map(|v| DataValue::Number(*v)).collect();
let cv = CommunicationValue::new(CommunicationType::get_states)
@ -675,4 +633,27 @@ pub async fn user_states(user_id: i64, user_ids: Vec<i64>) {
);
OMEGA_CONNECTION.send_message(&cv).await;
}
}
// ============================================================================
// Global Instance
// ============================================================================
static OMEGA_CONNECTION: Lazy<Arc<OmegaConnection>> = Lazy::new(|| {
let conn = Arc::new(OmegaConnection::new());
// Start the connection manager immediately
let conn_clone = conn.clone();
tokio::spawn(async move {
conn_clone.start().await;
});
start_task_cleanup_loop();
conn
});
pub fn get_omega_connection() -> Arc<OmegaConnection> {
OMEGA_CONNECTION.clone()
}