[Upd] mtp update
This commit is contained in:
parent
6c2211f8c4
commit
b2f3ed12f3
9 changed files with 475 additions and 842 deletions
1005
Cargo.lock
generated
1005
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -5,20 +5,21 @@ edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
mtp = { git = "https://git.methanium.net/methanium/mtp", features = [
|
mtp = { git = "https://git.methanium.net/methanium/mtp", features = [
|
||||||
"host",
|
|
||||||
"crypto",
|
"crypto",
|
||||||
"files",
|
"files",
|
||||||
|
"web-server",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
actix-web = { version = "4.12.1", features = ["rustls-0_23"] }
|
|
||||||
aes-gcm = "*"
|
aes-gcm = "*"
|
||||||
ansi_term = "0.12.1"
|
ansi_term = "0.12.1"
|
||||||
anyhow = "1.0.101"
|
anyhow = "1.0.101"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
|
bytes = "1"
|
||||||
dashmap = "6.1.0"
|
dashmap = "6.1.0"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
hkdf = "0.12.4"
|
hkdf = "0.12.4"
|
||||||
|
http = "1"
|
||||||
json = "0.12.4"
|
json = "0.12.4"
|
||||||
once_cell = "1.21.3"
|
once_cell = "1.21.3"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
|
|
@ -30,7 +31,6 @@ rustls = { version = "0.23.37", default-features = false, features = [
|
||||||
"aws-lc-rs",
|
"aws-lc-rs",
|
||||||
"prefer-post-quantum",
|
"prefer-post-quantum",
|
||||||
] }
|
] }
|
||||||
rustls-pemfile = "2.2.0"
|
|
||||||
sha2 = "0.10.9"
|
sha2 = "0.10.9"
|
||||||
sqlx = { version = "0.8.6", features = ["mysql", "runtime-async-std"] }
|
sqlx = { version = "0.8.6", features = ["mysql", "runtime-async-std"] }
|
||||||
strum = "0.27.2"
|
strum = "0.27.2"
|
||||||
|
|
@ -40,6 +40,3 @@ uuid = { version = "1.19.0", features = ["v4"] }
|
||||||
x448 = "0.6.0"
|
x448 = "0.6.0"
|
||||||
zip = "6.0.0"
|
zip = "6.0.0"
|
||||||
thiserror = "2.0.18"
|
thiserror = "2.0.18"
|
||||||
mtp-crypto = { git = "https://git.methanium.net/methanium/mtp", version = "0.1.0", features = [
|
|
||||||
"pqc",
|
|
||||||
] }
|
|
||||||
|
|
|
||||||
28
src/main.rs
28
src/main.rs
|
|
@ -10,8 +10,8 @@ use crate::util::file_util::get_directory;
|
||||||
use crate::util::logger::PrintType;
|
use crate::util::logger::PrintType;
|
||||||
use crate::util::logger::startup;
|
use crate::util::logger::startup;
|
||||||
use dotenv::from_path;
|
use dotenv::from_path;
|
||||||
use mtp::files::{load_keyring as load_keyring_file, save_keyring, save_public_key_bundle};
|
use mtp::files::{load_keyring_raw, save_keyring_raw, save_public_key_bundle};
|
||||||
use mtp_crypto::Keyring;
|
use mtp::crypto::Keyring;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use rustls::crypto::aws_lc_rs::default_provider;
|
use rustls::crypto::aws_lc_rs::default_provider;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
@ -20,9 +20,9 @@ use std::path::Path;
|
||||||
const KEYRING_PATH: &str = "./omega.mk";
|
const KEYRING_PATH: &str = "./omega.mk";
|
||||||
|
|
||||||
static KEYRING: Lazy<Keyring> = Lazy::new(|| {
|
static KEYRING: Lazy<Keyring> = Lazy::new(|| {
|
||||||
load_keyring_file(KEYRING_PATH).unwrap_or_else(|_| {
|
load_keyring_raw(KEYRING_PATH).unwrap_or_else(|_| {
|
||||||
let kr = Keyring::generate();
|
let kr = Keyring::generate();
|
||||||
save_keyring(&kr, KEYRING_PATH).expect("Failed to save generated keyring");
|
save_keyring_raw(&kr, KEYRING_PATH).expect("Failed to save generated keyring");
|
||||||
save_public_key_bundle(&kr.public_key_bundle(), KEYRING_PATH)
|
save_public_key_bundle(&kr.public_key_bundle(), KEYRING_PATH)
|
||||||
.expect("Failed to save generated public key bundle");
|
.expect("Failed to save generated public key bundle");
|
||||||
eprintln!("Generated new keyring at {}", KEYRING_PATH);
|
eprintln!("Generated new keyring at {}", KEYRING_PATH);
|
||||||
|
|
@ -48,18 +48,6 @@ async fn main() {
|
||||||
log_in!("Incoming messages");
|
log_in!("Incoming messages");
|
||||||
log_out!("Outgoing messages");
|
log_out!("Outgoing messages");
|
||||||
|
|
||||||
let omikron_port: u16 = env::var("OMIKRON_PORT")
|
|
||||||
.ok()
|
|
||||||
.and_then(|s| s.parse().ok())
|
|
||||||
.unwrap_or(9187);
|
|
||||||
|
|
||||||
let omikron_handle = tokio::spawn(async move {
|
|
||||||
match omikron_connection::start(omikron_port).await {
|
|
||||||
Err(e) => log_err!(0, PrintType::General, "{:?}", e),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
log!("Started");
|
log!("Started");
|
||||||
log!(" .env");
|
log!(" .env");
|
||||||
if let Err(e) = initialize_db().await {
|
if let Err(e) = initialize_db().await {
|
||||||
|
|
@ -77,13 +65,13 @@ async fn main() {
|
||||||
log!(" Users");
|
log!(" Users");
|
||||||
}
|
}
|
||||||
|
|
||||||
let api_port: u16 = env::var("API_PORT")
|
let port: u16 = env::var("PORT")
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|s| s.parse().ok())
|
.and_then(|s| s.parse().ok())
|
||||||
.unwrap_or(9188);
|
.unwrap_or(443);
|
||||||
|
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
result = server::server::start(api_port) => {
|
result = omikron_connection::start(port) => {
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
log_err!(0, PrintType::General, "Server error: {:?}", e);
|
log_err!(0, PrintType::General, "Server error: {:?}", e);
|
||||||
}
|
}
|
||||||
|
|
@ -92,6 +80,4 @@ async fn main() {
|
||||||
log!("Shutting down on signal...");
|
log!("Shutting down on signal...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
omikron_handle.abort();
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,31 @@ use crate::sql::sql::{get_by_user_id, get_iota_by_id, get_omikron_by_id};
|
||||||
use crate::sql::user_online_tracker::{get_all_connections, get_iota_primary_omikron_connection};
|
use crate::sql::user_online_tracker::{get_all_connections, get_iota_primary_omikron_connection};
|
||||||
use crate::transport::omikron_manager::get_random_omikron;
|
use crate::transport::omikron_manager::get_random_omikron;
|
||||||
use crate::util::file_util::get_directory;
|
use crate::util::file_util::get_directory;
|
||||||
use actix_web::HttpResponse;
|
|
||||||
use actix_web::http::{StatusCode, header};
|
|
||||||
use base64::Engine as _;
|
use base64::Engine as _;
|
||||||
|
use bytes::Bytes;
|
||||||
|
use http::{Method, StatusCode};
|
||||||
use json::JsonValue;
|
use json::JsonValue;
|
||||||
|
use mtp::webserver::{Http3Request, Http3Response, RouteParams};
|
||||||
|
|
||||||
pub async fn handle(path: &str, body_string: Option<String>) -> HttpResponse {
|
pub async fn handle(request: Http3Request, response: Http3Response) -> Http3Response {
|
||||||
if path == "OPTIONS" {
|
let method = request.method;
|
||||||
return HttpResponse::Ok()
|
let path = request.uri.path().to_string();
|
||||||
.insert_header(("Access-Control-Allow-Origin", "*"))
|
let body_string = request
|
||||||
.insert_header(("Access-Control-Allow-Methods", "GET, POST, OPTIONS"))
|
.body
|
||||||
.insert_header(("Access-Control-Allow-Headers", "*"))
|
.map(|body| String::from_utf8_lossy(&body).to_string());
|
||||||
.finish();
|
|
||||||
|
if method == Method::OPTIONS {
|
||||||
|
return response
|
||||||
|
.status(StatusCode::OK)
|
||||||
|
.header("access-control-allow-origin", "*")
|
||||||
|
.header("access-control-allow-methods", "GET, POST, OPTIONS")
|
||||||
|
.header("access-control-allow-headers", "*");
|
||||||
}
|
}
|
||||||
|
|
||||||
let path_parts: Vec<&str> = path.split("/").filter(|s| !s.is_empty()).collect();
|
let path_parts: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
|
||||||
|
|
||||||
let _body: Option<JsonValue> = if body_string.is_some() {
|
let _body: Option<JsonValue> = if let Some(ref bs) = body_string {
|
||||||
if let Ok(body_json) = json::parse(&body_string.unwrap()) {
|
if let Ok(body_json) = json::parse(bs) {
|
||||||
Some(body_json)
|
Some(body_json)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
@ -39,20 +46,22 @@ pub async fn handle(path: &str, body_string: Option<String>) -> HttpResponse {
|
||||||
|
|
||||||
match std::fs::read(file_path) {
|
match std::fs::read(file_path) {
|
||||||
Ok(file_bytes) => {
|
Ok(file_bytes) => {
|
||||||
return HttpResponse::Ok()
|
return response
|
||||||
.insert_header(("Access-Control-Allow-Origin", "*"))
|
.status(StatusCode::OK)
|
||||||
.insert_header(("Content-Type", "application/zip"))
|
.header("access-control-allow-origin", "*")
|
||||||
.insert_header((
|
.header("content-type", "application/zip")
|
||||||
"Content-Disposition",
|
.header(
|
||||||
|
"content-disposition",
|
||||||
"attachment; filename=\"iota_frontend.zip\"",
|
"attachment; filename=\"iota_frontend.zip\"",
|
||||||
))
|
)
|
||||||
.body(file_bytes);
|
.body(Bytes::from(file_bytes));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let mut res = JsonValue::new_object();
|
let mut res = JsonValue::new_object();
|
||||||
res["status"] = "error_not_found".into();
|
res["status"] = "error_not_found".into();
|
||||||
return HttpResponse::NotFound()
|
return response
|
||||||
.insert_header(("Access-Control-Allow-Origin", "*"))
|
.status(StatusCode::NOT_FOUND)
|
||||||
|
.header("access-control-allow-origin", "*")
|
||||||
.body(res.dump());
|
.body(res.dump());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -309,6 +318,23 @@ pub async fn handle(path: &str, body_string: Option<String>) -> HttpResponse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================================================
|
||||||
|
// DIRECT - SHORT LINK RESOLUTION
|
||||||
|
// ==================================================
|
||||||
|
["direct", short @ ..] => {
|
||||||
|
let short_str = short.join("/");
|
||||||
|
let short = short_str.replace("/", "");
|
||||||
|
if let Ok(long) = crate::server::short_link::get_short_link(&short).await {
|
||||||
|
return response
|
||||||
|
.status(StatusCode::TEMPORARY_REDIRECT)
|
||||||
|
.header("location", &long);
|
||||||
|
} else {
|
||||||
|
return response
|
||||||
|
.status(StatusCode::TEMPORARY_REDIRECT)
|
||||||
|
.header("location", "https://tensamin.net");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ==================================================
|
// ==================================================
|
||||||
// DEFAULT
|
// DEFAULT
|
||||||
// ==================================================
|
// ==================================================
|
||||||
|
|
@ -318,11 +344,19 @@ pub async fn handle(path: &str, body_string: Option<String>) -> HttpResponse {
|
||||||
(StatusCode::INTERNAL_SERVER_ERROR, res.dump())
|
(StatusCode::INTERNAL_SERVER_ERROR, res.dump())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let body_bytes = body_text.into_bytes();
|
|
||||||
|
|
||||||
HttpResponse::build(status)
|
response
|
||||||
.insert_header((header::ACCESS_CONTROL_ALLOW_ORIGIN, "*"))
|
.status(status)
|
||||||
.insert_header((header::ACCESS_CONTROL_ALLOW_HEADERS, "*"))
|
.header("access-control-allow-origin", "*")
|
||||||
.insert_header((header::ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, OPTIONS"))
|
.header("access-control-allow-headers", "*")
|
||||||
.body(body_bytes)
|
.header("access-control-allow-methods", "GET, POST, OPTIONS")
|
||||||
|
.body(body_text)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn handle_pattern(
|
||||||
|
request: Http3Request,
|
||||||
|
response: Http3Response,
|
||||||
|
_params: RouteParams,
|
||||||
|
) -> Http3Response {
|
||||||
|
handle(request, response).await
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use actix_web::{HttpResponse, Responder};
|
use http::StatusCode;
|
||||||
|
use mtp::webserver::Http3Response;
|
||||||
|
|
||||||
pub async fn index_handler() -> impl Responder {
|
pub fn index_handler(response: Http3Response) -> Http3Response {
|
||||||
let documentation = r#"
|
let documentation = r#"
|
||||||
Omega API Server
|
Omega API Server
|
||||||
|
|
||||||
|
|
@ -11,7 +12,8 @@ Available Routes:
|
||||||
All other routes will return this documentation.
|
All other routes will return this documentation.
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
HttpResponse::Ok()
|
response
|
||||||
.content_type("text/plain; charset=utf-8")
|
.status(StatusCode::OK)
|
||||||
|
.header("content-type", "text/plain; charset=utf-8")
|
||||||
.body(documentation)
|
.body(documentation)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,69 +1,19 @@
|
||||||
use crate::{
|
use crate::server::{api, index::index_handler};
|
||||||
log,
|
use mtp::webserver::WebServerConfig;
|
||||||
server::{api, index::index_handler, short_link::get_short_link},
|
|
||||||
util::file_util::load_file_buf,
|
|
||||||
};
|
|
||||||
|
|
||||||
use actix_web::{App, HttpRequest, HttpResponse, HttpServer, Responder, http::header, web};
|
pub fn build_web_config() -> Result<WebServerConfig, mtp::webserver::RouterError> {
|
||||||
|
WebServerConfig::new()
|
||||||
use rustls::ServerConfig;
|
.route(
|
||||||
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
|
"/",
|
||||||
use rustls_pemfile::{certs, pkcs8_private_keys};
|
|_request, response| async move { index_handler(response) },
|
||||||
|
)?
|
||||||
pub async fn start(port: u16) -> anyhow::Result<()> {
|
.route("/api/download/iota_frontend", api::handle)?
|
||||||
let mut cert_reader = load_file_buf("certs", "server_cert.pem")?;
|
.route("/api/get/omikron", api::handle)?
|
||||||
|
.route("/api/get/connections", api::handle)?
|
||||||
let mut key_reader = load_file_buf("certs", "server_key.pem")?;
|
.route("/api/get/public_key", api::handle)?
|
||||||
|
.route_pattern("/api/get/omikron/{id}", api::handle_pattern)?
|
||||||
let cert_chain: Vec<CertificateDer<'static>> =
|
.route_pattern("/api/get/iota/{id}", api::handle_pattern)?
|
||||||
certs(&mut cert_reader).collect::<Result<_, _>>()?;
|
.route_pattern("/api/get/id/{username}", api::handle_pattern)?
|
||||||
|
.route_pattern("/api/get/user/{id}", api::handle_pattern)?
|
||||||
let mut keys: Vec<PrivateKeyDer<'static>> = pkcs8_private_keys(&mut key_reader)
|
.route_pattern("/direct/{short}", api::handle_pattern)
|
||||||
.map(|res| res.map(Into::into))
|
|
||||||
.collect::<Result<_, _>>()?;
|
|
||||||
|
|
||||||
let key = keys.remove(0);
|
|
||||||
|
|
||||||
let mut config = ServerConfig::builder()
|
|
||||||
.with_no_client_auth()
|
|
||||||
.with_single_cert(cert_chain, key)?;
|
|
||||||
|
|
||||||
config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
|
|
||||||
|
|
||||||
let bind_addr = std::env::var("BIND_ADDRESS").unwrap_or_else(|_| "0.0.0.0".to_string());
|
|
||||||
let addr = format!("{}:{}", bind_addr, port);
|
|
||||||
log!(" Server on {}", addr);
|
|
||||||
|
|
||||||
HttpServer::new(move || {
|
|
||||||
App::new()
|
|
||||||
.route("/api/{path:.*}", web::to(api_handler))
|
|
||||||
.route("/direct/{path:.*}", web::to(direct_handler))
|
|
||||||
.default_service(web::to(index_handler))
|
|
||||||
})
|
|
||||||
.bind_rustls_0_23(addr, config)?
|
|
||||||
.run()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
async fn direct_handler(req: HttpRequest) -> impl Responder {
|
|
||||||
let path = req.uri().path().to_string();
|
|
||||||
let short = path.replace("/direct/", "");
|
|
||||||
|
|
||||||
if let Ok(long) = get_short_link(&short).await {
|
|
||||||
HttpResponse::TemporaryRedirect()
|
|
||||||
.append_header((header::LOCATION, long))
|
|
||||||
.finish()
|
|
||||||
} else {
|
|
||||||
HttpResponse::TemporaryRedirect()
|
|
||||||
.append_header((header::LOCATION, "https://tensamin.net"))
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn api_handler(req: HttpRequest, body: web::Bytes) -> HttpResponse {
|
|
||||||
let path = req.uri().path().to_string();
|
|
||||||
let body_string = String::from_utf8_lossy(&body).to_string();
|
|
||||||
|
|
||||||
api::handle(&path, Some(body_string)).await
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::log;
|
use crate::log;
|
||||||
use mtp_crypto::PublicKeyBundle;
|
use mtp::crypto::PublicKeyBundle;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use sqlx::{MySql, Pool, Row, mysql::MySqlPoolOptions};
|
use sqlx::{MySql, Pool, Row, mysql::MySqlPoolOptions};
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
|
@ -607,7 +607,10 @@ pub async fn create_new_iota(public_key: PublicKeyBundle) -> Result<i64, sqlx::E
|
||||||
Ok(new_id)
|
Ok(new_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn register_complete_iota(id: i64, public_key: PublicKeyBundle) -> Result<(), sqlx::Error> {
|
pub async fn register_complete_iota(
|
||||||
|
id: i64,
|
||||||
|
public_key: PublicKeyBundle,
|
||||||
|
) -> Result<(), sqlx::Error> {
|
||||||
let pool = {
|
let pool = {
|
||||||
let db_lock = SQL_DB.read().await;
|
let db_lock = SQL_DB.read().await;
|
||||||
db_lock
|
db_lock
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
load_keyring, log, log_cv_in, log_cv_out, log_err, log_in,
|
load_keyring, log, log_cv_in, log_cv_out, log_err, log_in,
|
||||||
server::short_link::add_short_link,
|
server::{self, short_link::add_short_link},
|
||||||
sql::{
|
sql::{
|
||||||
connection_status::UserStatus,
|
connection_status::UserStatus,
|
||||||
sql::{self, get_by_user_id, get_by_username, get_iota_by_id},
|
sql::{self, get_by_user_id, get_by_username, get_iota_by_id},
|
||||||
|
|
@ -11,12 +11,10 @@ use crate::{
|
||||||
};
|
};
|
||||||
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use mtp::host::{AuthenticationPolicy, Receiver, Sender};
|
use mtp::codec::{CommunicationType, CommunicationValue, DataType, DataValue};
|
||||||
use mtp::{
|
use mtp::host::{AuthenticationPolicy, HostConfig, Policy, SendMode};
|
||||||
codec::{CommunicationType, CommunicationValue, DataType, DataValue},
|
use mtp::webserver::{MTPWebServer, WebMtpReceiver, WebMtpSender};
|
||||||
host::{Host, HostConfig, Policy, SendMode},
|
use mtp::crypto::PublicKeyBundle;
|
||||||
};
|
|
||||||
use mtp_crypto::PublicKeyBundle;
|
|
||||||
use std::net::{IpAddr, Ipv4Addr};
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
use std::{
|
use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
|
@ -26,17 +24,10 @@ use tokio::{
|
||||||
sync::{Mutex, RwLock},
|
sync::{Mutex, RwLock},
|
||||||
time::interval,
|
time::interval,
|
||||||
};
|
};
|
||||||
// ============================================================================
|
|
||||||
// Configuration
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
const CLEANUP_INTERVAL: Duration = Duration::from_secs(30);
|
const CLEANUP_INTERVAL: Duration = Duration::from_secs(30);
|
||||||
const MAX_WAITING_AGE: Duration = Duration::from_secs(60);
|
const MAX_WAITING_AGE: Duration = Duration::from_secs(60);
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Error Types
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum OmikronError {
|
pub enum OmikronError {
|
||||||
#[error("Not connected")]
|
#[error("Not connected")]
|
||||||
|
|
@ -55,22 +46,14 @@ pub enum OmikronError {
|
||||||
|
|
||||||
pub type OmikronResult<T> = Result<T, OmikronError>;
|
pub type OmikronResult<T> = Result<T, OmikronError>;
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Waiting Task System (Preserved from original)
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
pub struct WaitingTask {
|
pub struct WaitingTask {
|
||||||
pub task: Box<dyn Fn(Arc<OmikronConnection>, CommunicationValue) -> bool + Send + Sync>,
|
pub task: Box<dyn Fn(Arc<OmikronConnection>, CommunicationValue) -> bool + Send + Sync>,
|
||||||
pub inserted_at: Instant,
|
pub inserted_at: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Omikron Connection (mtp/QUIC-based)
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
pub struct OmikronConnection {
|
pub struct OmikronConnection {
|
||||||
id: u64,
|
id: u64,
|
||||||
sender: Mutex<Option<Sender>>,
|
sender: Mutex<Option<WebMtpSender>>,
|
||||||
pub ping: RwLock<i64>,
|
pub ping: RwLock<i64>,
|
||||||
waiting_tasks: DashMap<u32, WaitingTask>,
|
waiting_tasks: DashMap<u32, WaitingTask>,
|
||||||
cleanup_handle: std::sync::Mutex<Option<tokio::task::JoinHandle<()>>>,
|
cleanup_handle: std::sync::Mutex<Option<tokio::task::JoinHandle<()>>>,
|
||||||
|
|
@ -85,11 +68,7 @@ impl Drop for OmikronConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OmikronConnection {
|
impl OmikronConnection {
|
||||||
// -------------------------------------------------------------------------
|
pub fn new(sender: WebMtpSender, id: u64) -> Arc<Self> {
|
||||||
// Construction
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
pub fn new(sender: Sender, id: u64) -> Arc<Self> {
|
|
||||||
let conn = Arc::new(Self {
|
let conn = Arc::new(Self {
|
||||||
id,
|
id,
|
||||||
sender: Mutex::new(Some(sender)),
|
sender: Mutex::new(Some(sender)),
|
||||||
|
|
@ -101,18 +80,13 @@ impl OmikronConnection {
|
||||||
conn
|
conn
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
pub async fn handle(self: Arc<Self>, receiver: &mut WebMtpReceiver) {
|
||||||
// Main Handler Loop
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
pub async fn handle(self: Arc<Self>, receiver: &mut Receiver) {
|
|
||||||
log_in!(
|
log_in!(
|
||||||
self.id as i64,
|
self.id as i64,
|
||||||
PrintType::Omega,
|
PrintType::Omega,
|
||||||
"Omikron connection started"
|
"Omikron connection started"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Start cleanup task
|
|
||||||
let cleanup_conn = self.clone();
|
let cleanup_conn = self.clone();
|
||||||
let cleanup_handle = tokio::spawn(async move {
|
let cleanup_handle = tokio::spawn(async move {
|
||||||
let mut ticker = interval(CLEANUP_INTERVAL);
|
let mut ticker = interval(CLEANUP_INTERVAL);
|
||||||
|
|
@ -142,10 +116,6 @@ impl OmikronConnection {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Message Processing
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
async fn process_message(self: Arc<Self>, cv: CommunicationValue) -> OmikronResult<()> {
|
async fn process_message(self: Arc<Self>, cv: CommunicationValue) -> OmikronResult<()> {
|
||||||
if !cv.is_type(CommunicationType::Pong) && !cv.is_type(CommunicationType::Ping) {
|
if !cv.is_type(CommunicationType::Pong) && !cv.is_type(CommunicationType::Ping) {
|
||||||
log_cv_in!(PrintType::Omikron, &cv);
|
log_cv_in!(PrintType::Omikron, &cv);
|
||||||
|
|
@ -153,18 +123,15 @@ impl OmikronConnection {
|
||||||
|
|
||||||
let msg_id = cv.get_id();
|
let msg_id = cv.get_id();
|
||||||
|
|
||||||
// Check waiting tasks first (response to previous request)
|
|
||||||
if let Some((_, task)) = self.waiting_tasks.remove(&msg_id) {
|
if let Some((_, task)) = self.waiting_tasks.remove(&msg_id) {
|
||||||
let _ = (task.task)(self.clone(), cv);
|
let _ = (task.task)(self.clone(), cv);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle ping regardless of message type
|
|
||||||
if cv.is_type(CommunicationType::Ping) {
|
if cv.is_type(CommunicationType::Ping) {
|
||||||
return self.handle_ping(cv).await;
|
return self.handle_ping(cv).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authentication is completed by the mtp host before this connection exists.
|
|
||||||
let omikron_id = self.id as i64;
|
let omikron_id = self.id as i64;
|
||||||
self.clone().handle_authenticated(cv, omikron_id).await
|
self.clone().handle_authenticated(cv, omikron_id).await
|
||||||
}
|
}
|
||||||
|
|
@ -176,10 +143,8 @@ impl OmikronConnection {
|
||||||
) -> OmikronResult<()> {
|
) -> OmikronResult<()> {
|
||||||
let comm_type = cv.get_comm_type_enum();
|
let comm_type = cv.get_comm_type_enum();
|
||||||
match comm_type {
|
match comm_type {
|
||||||
// Link shortening
|
|
||||||
Some(CommunicationType::ShortenLink) => self.handle_shorten_link(cv).await,
|
Some(CommunicationType::ShortenLink) => self.handle_shorten_link(cv).await,
|
||||||
|
|
||||||
// Online status tracking
|
|
||||||
Some(CommunicationType::UserConnected) => {
|
Some(CommunicationType::UserConnected) => {
|
||||||
self.handle_user_connected(cv, omikron_id).await;
|
self.handle_user_connected(cv, omikron_id).await;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -234,10 +199,6 @@ impl OmikronConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Specific Handlers (ported from original WebSocket implementation)
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
async fn handle_shorten_link(self: Arc<Self>, cv: CommunicationValue) -> OmikronResult<()> {
|
async fn handle_shorten_link(self: Arc<Self>, cv: CommunicationValue) -> OmikronResult<()> {
|
||||||
let link = cv
|
let link = cv
|
||||||
.get_data(DataType::Link)
|
.get_data(DataType::Link)
|
||||||
|
|
@ -348,7 +309,6 @@ impl OmikronConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_get_user_data(self: Arc<Self>, cv: CommunicationValue) -> OmikronResult<()> {
|
async fn handle_get_user_data(self: Arc<Self>, cv: CommunicationValue) -> OmikronResult<()> {
|
||||||
// Try by user_id first
|
|
||||||
if let Some(user_id) = cv.get_data(DataType::UserId).as_number() {
|
if let Some(user_id) = cv.get_data(DataType::UserId).as_number() {
|
||||||
if let Ok(user_data) = get_by_user_id(user_id as i64).await {
|
if let Ok(user_data) = get_by_user_id(user_id as i64).await {
|
||||||
let response = self
|
let response = self
|
||||||
|
|
@ -359,7 +319,6 @@ impl OmikronConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try by username
|
|
||||||
if let Some(username) = cv.get_data(DataType::Username).as_str() {
|
if let Some(username) = cv.get_data(DataType::Username).as_str() {
|
||||||
if let Ok(user_data) = get_by_username(username).await {
|
if let Ok(user_data) = get_by_username(username).await {
|
||||||
let response = self
|
let response = self
|
||||||
|
|
@ -370,7 +329,6 @@ impl OmikronConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not found
|
|
||||||
let response =
|
let response =
|
||||||
CommunicationValue::new(CommunicationType::ErrorNotFound).with_id(cv.get_id());
|
CommunicationValue::new(CommunicationType::ErrorNotFound).with_id(cv.get_id());
|
||||||
self.send(&response).await
|
self.send(&response).await
|
||||||
|
|
@ -421,11 +379,9 @@ impl OmikronConnection {
|
||||||
)
|
)
|
||||||
.add_typed_default(DataType::SubEnd, DataValue::SignedNumber(sub_end.into()));
|
.add_typed_default(DataType::SubEnd, DataValue::SignedNumber(sub_end.into()));
|
||||||
|
|
||||||
// Display name (fallback to username)
|
|
||||||
let display_name = display.filter(|d| !d.is_empty()).unwrap_or(username);
|
let display_name = display.filter(|d| !d.is_empty()).unwrap_or(username);
|
||||||
response = response.add_typed_default(DataType::Display, DataValue::Str(display_name));
|
response = response.add_typed_default(DataType::Display, DataValue::Str(display_name));
|
||||||
|
|
||||||
// Optional fields
|
|
||||||
if let Some(s) = status.filter(|s| !s.is_empty()) {
|
if let Some(s) = status.filter(|s| !s.is_empty()) {
|
||||||
response = response.add_typed_default(DataType::Status, DataValue::Str(s));
|
response = response.add_typed_default(DataType::Status, DataValue::Str(s));
|
||||||
}
|
}
|
||||||
|
|
@ -437,7 +393,6 @@ impl OmikronConnection {
|
||||||
response.add_typed_default(DataType::Avatar, DataValue::Str(STANDARD.encode(av)));
|
response.add_typed_default(DataType::Avatar, DataValue::Str(STANDARD.encode(av)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Online status
|
|
||||||
let user_status = user_online_tracker::get_user_status(id);
|
let user_status = user_online_tracker::get_user_status(id);
|
||||||
let iota_connections =
|
let iota_connections =
|
||||||
user_online_tracker::get_iota_omikron_connections(iota_id).unwrap_or_default();
|
user_online_tracker::get_iota_omikron_connections(iota_id).unwrap_or_default();
|
||||||
|
|
@ -477,7 +432,6 @@ impl OmikronConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_get_iota_data(self: Arc<Self>, cv: CommunicationValue) -> OmikronResult<()> {
|
async fn handle_get_iota_data(self: Arc<Self>, cv: CommunicationValue) -> OmikronResult<()> {
|
||||||
// Try by iota_id
|
|
||||||
if let Some(iota_id) = cv.get_data(DataType::IotaId).as_number() {
|
if let Some(iota_id) = cv.get_data(DataType::IotaId).as_number() {
|
||||||
if let Ok((iota_id, public_key)) = get_iota_by_id(iota_id as i64).await {
|
if let Ok((iota_id, public_key)) = get_iota_by_id(iota_id as i64).await {
|
||||||
let response = self
|
let response = self
|
||||||
|
|
@ -488,7 +442,6 @@ impl OmikronConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try by user_id
|
|
||||||
if let Some(user_id) = cv.get_data(DataType::UserId).as_number() {
|
if let Some(user_id) = cv.get_data(DataType::UserId).as_number() {
|
||||||
if let Ok((_, iota_id, _, _, _, _, _, _, _, _, _, _)) =
|
if let Ok((_, iota_id, _, _, _, _, _, _, _, _, _, _)) =
|
||||||
get_by_user_id(user_id as i64).await
|
get_by_user_id(user_id as i64).await
|
||||||
|
|
@ -509,7 +462,6 @@ impl OmikronConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try by username
|
|
||||||
if let Some(username) = cv.get_data(DataType::Username).as_str() {
|
if let Some(username) = cv.get_data(DataType::Username).as_str() {
|
||||||
if let Ok((user_id, iota_id, _, _, _, _, _, _, _, _, _, _)) =
|
if let Ok((user_id, iota_id, _, _, _, _, _, _, _, _, _, _)) =
|
||||||
get_by_username(username).await
|
get_by_username(username).await
|
||||||
|
|
@ -594,7 +546,6 @@ impl OmikronConnection {
|
||||||
|
|
||||||
if let Some(public_key) = public_key {
|
if let Some(public_key) = public_key {
|
||||||
if let Some(iota_id) = iota_id_opt {
|
if let Some(iota_id) = iota_id_opt {
|
||||||
// Register existing IOTA
|
|
||||||
match sql::register_complete_iota(iota_id, public_key).await {
|
match sql::register_complete_iota(iota_id, public_key).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let response = CommunicationValue::new(CommunicationType::Success)
|
let response = CommunicationValue::new(CommunicationType::Success)
|
||||||
|
|
@ -609,7 +560,6 @@ impl OmikronConnection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Create new IOTA
|
|
||||||
match sql::create_new_iota(public_key).await {
|
match sql::create_new_iota(public_key).await {
|
||||||
Ok(new_iota_id) => {
|
Ok(new_iota_id) => {
|
||||||
let response =
|
let response =
|
||||||
|
|
@ -681,7 +631,6 @@ impl OmikronConnection {
|
||||||
let mut success = true;
|
let mut success = true;
|
||||||
let mut error_message = String::new();
|
let mut error_message = String::new();
|
||||||
|
|
||||||
// Process each field
|
|
||||||
if let Some(username) = cv.get_data(DataType::Username).as_str() {
|
if let Some(username) = cv.get_data(DataType::Username).as_str() {
|
||||||
if let Err(e) = sql::change_username(user_id, username.to_string()).await {
|
if let Err(e) = sql::change_username(user_id, username.to_string()).await {
|
||||||
success = false;
|
success = false;
|
||||||
|
|
@ -747,7 +696,7 @@ impl OmikronConnection {
|
||||||
) {
|
) {
|
||||||
match sql::get_by_user_id(user_id).await {
|
match sql::get_by_user_id(user_id).await {
|
||||||
Ok(user) => {
|
Ok(user) => {
|
||||||
let current_token = user.11; // reset_token field
|
let current_token = user.11;
|
||||||
if current_token == reset_token {
|
if current_token == reset_token {
|
||||||
let mut success = true;
|
let mut success = true;
|
||||||
let mut error_message = String::new();
|
let mut error_message = String::new();
|
||||||
|
|
@ -889,7 +838,6 @@ impl OmikronConnection {
|
||||||
.with_id(cv.get_id());
|
.with_id(cv.get_id());
|
||||||
let _ = self.send(&response).await;
|
let _ = self.send(&response).await;
|
||||||
|
|
||||||
// Sync with other Omikron clients
|
|
||||||
let sync_cv = CommunicationValue::new(CommunicationType::ReadNotification)
|
let sync_cv = CommunicationValue::new(CommunicationType::ReadNotification)
|
||||||
.with_receiver(receiver_id as u64)
|
.with_receiver(receiver_id as u64)
|
||||||
.add_typed_default(
|
.add_typed_default(
|
||||||
|
|
@ -926,7 +874,6 @@ impl OmikronConnection {
|
||||||
CommunicationValue::new(CommunicationType::PushNotification).with_id(cv.get_id());
|
CommunicationValue::new(CommunicationType::PushNotification).with_id(cv.get_id());
|
||||||
let _ = self.send(&response).await;
|
let _ = self.send(&response).await;
|
||||||
|
|
||||||
// Sync with other Omikron clients
|
|
||||||
let push_cv = CommunicationValue::new(CommunicationType::PushNotification)
|
let push_cv = CommunicationValue::new(CommunicationType::PushNotification)
|
||||||
.with_receiver(receiver_id as u64)
|
.with_receiver(receiver_id as u64)
|
||||||
.add_typed_default(
|
.add_typed_default(
|
||||||
|
|
@ -985,10 +932,6 @@ impl OmikronConnection {
|
||||||
self.send(&response).await
|
self.send(&response).await
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Utilities
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
async fn send(self: Arc<Self>, cv: &CommunicationValue) -> OmikronResult<()> {
|
async fn send(self: Arc<Self>, cv: &CommunicationValue) -> OmikronResult<()> {
|
||||||
if !cv.is_type(CommunicationType::Pong) && !cv.is_type(CommunicationType::Ping) {
|
if !cv.is_type(CommunicationType::Pong) && !cv.is_type(CommunicationType::Ping) {
|
||||||
log_cv_out!(PrintType::Omikron, cv);
|
log_cv_out!(PrintType::Omikron, cv);
|
||||||
|
|
@ -1060,9 +1003,10 @@ pub async fn complete_register(_pub_key: PublicKeyBundle, _description: Option<S
|
||||||
|
|
||||||
pub async fn start(port: u16) -> Result<(), Box<dyn std::error::Error>> {
|
pub async fn start(port: u16) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let cert_pem = load_file_vec("certs", "transport_cert.pem").expect("Error loading Pemfile");
|
let cert_pem = load_file_vec("certs", "transport_cert.pem").expect("Error loading Pemfile");
|
||||||
|
|
||||||
let key_pem = load_file_vec("certs", "transport_key.pem").expect("Error loading Keyfile");
|
let key_pem = load_file_vec("certs", "transport_key.pem").expect("Error loading Keyfile");
|
||||||
|
|
||||||
|
let web_config = server::server::build_web_config()?;
|
||||||
|
|
||||||
let host_config = HostConfig::new(
|
let host_config = HostConfig::new(
|
||||||
IpAddr::from(Ipv4Addr::new(0, 0, 0, 0)),
|
IpAddr::from(Ipv4Addr::new(0, 0, 0, 0)),
|
||||||
port,
|
port,
|
||||||
|
|
@ -1072,6 +1016,7 @@ pub async fn start(port: u16) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
.with_policy(Policy {
|
.with_policy(Policy {
|
||||||
send_mode: SendMode::SingleStreamPerMessage,
|
send_mode: SendMode::SingleStreamPerMessage,
|
||||||
max_message_size: 1_000_000_000,
|
max_message_size: 1_000_000_000,
|
||||||
|
handshake_max_message_size: 1_000_000,
|
||||||
close_frame_len: u32::MAX,
|
close_frame_len: u32::MAX,
|
||||||
application_close_code: 0,
|
application_close_code: 0,
|
||||||
open_stream_timeout: Duration::from_millis(2_000),
|
open_stream_timeout: Duration::from_millis(2_000),
|
||||||
|
|
@ -1081,9 +1026,11 @@ pub async fn start(port: u16) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
keep_alive_interval: Some(Duration::from_secs(6)),
|
keep_alive_interval: Some(Duration::from_secs(6)),
|
||||||
max_idle_timeout: Some(Duration::from_secs(30)),
|
max_idle_timeout: Some(Duration::from_secs(30)),
|
||||||
force_close_delay: Duration::from_millis(300),
|
force_close_delay: Duration::from_millis(300),
|
||||||
max_transient_recv_errors: 20,
|
|
||||||
transient_recv_backoff: Duration::from_millis(100),
|
|
||||||
receiver_queue_capacity: 1000,
|
receiver_queue_capacity: 1000,
|
||||||
|
max_concurrent_stream_tasks: 10,
|
||||||
|
persistent_stream_max_retries: 5,
|
||||||
|
persistent_stream_retry_backoff: Duration::from_secs(5),
|
||||||
|
max_frames_per_stream: None,
|
||||||
})
|
})
|
||||||
.with_authentication(
|
.with_authentication(
|
||||||
load_keyring(),
|
load_keyring(),
|
||||||
|
|
@ -1092,17 +1039,14 @@ pub async fn start(port: u16) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
)
|
)
|
||||||
.with_authentication_policy(AuthenticationPolicy::ForceAuthentication);
|
.with_authentication_policy(AuthenticationPolicy::ForceAuthentication);
|
||||||
|
|
||||||
let mut host: Host = Host::new(host_config).await?;
|
let mut server = MTPWebServer::new(host_config, web_config).await?;
|
||||||
log!("OmikronServer listening on port {}", port);
|
log!("OmegaServer listening on port {}", port);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut conn = match host.accept().await {
|
let mut conn = match server.accept().await {
|
||||||
Ok(Some(conn)) => conn,
|
Ok(Some(conn)) => conn,
|
||||||
Ok(None) => break,
|
Ok(None) => break,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// A single omikron's failed/aborted handshake (bad auth, a
|
|
||||||
// probe, a mid-handshake disconnect) must not take down the
|
|
||||||
// whole listener - only that connection attempt is lost.
|
|
||||||
log_err!(0, PrintType::Omega, "Rejected omikron connection: {}", e);
|
log_err!(0, PrintType::Omega, "Rejected omikron connection: {}", e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,7 @@ type_maps:
|
||||||
CallState: 49
|
CallState: 49
|
||||||
ScreenShare: 50
|
ScreenShare: 50
|
||||||
PrivateKeyHash: 51
|
PrivateKeyHash: 51
|
||||||
Accepted: 52
|
# Accepted: 52 now part of default MTP
|
||||||
AcceptedProfiles: 53
|
AcceptedProfiles: 53
|
||||||
DeniedProfiles: 54
|
DeniedProfiles: 54
|
||||||
Content: 55
|
Content: 55
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue