mtp update

This commit is contained in:
Alex Emmet 2026-07-20 15:27:52 +02:00
commit a642afce5a
12 changed files with 632 additions and 354 deletions

786
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -5,13 +5,11 @@ edition = "2024"
[dependencies] [dependencies]
mtp = { git = "https://git.methanium.net/Methanium/mtp.git", features = [ mtp = { git = "https://git.methanium.net/Methanium/mtp.git", features = [
"host", "web-server",
"client", "client",
"crypto", "crypto",
"files", "files",
] } ] }
# Needed directly for `ConnectionHandle`, which `mtp-client` does not re-export.
# Pulled in transitively already via `mtp`, so this just names the same crate/commit.
mtp-transport = { git = "https://git.methanium.net/Methanium/mtp.git" } mtp-transport = { git = "https://git.methanium.net/Methanium/mtp.git" }
ansi_term = "*" ansi_term = "*"
@ -21,7 +19,7 @@ dashmap = "*"
futures = "*" futures = "*"
once_cell = "1.21.4" once_cell = "1.21.4"
rand = "0.8" rand = "0.8"
rustls = { version = "0.23.40", default-features = false, features = [ rustls = { version = "0.23.42", default-features = false, features = [
"std", "std",
"tls12", "tls12",
"aws-lc-rs", "aws-lc-rs",
@ -32,10 +30,10 @@ log = "0.4"
dotenv = "0.15.0" dotenv = "0.15.0"
strum = "0.28.0" strum = "0.28.0"
strum_macros = "0.28.0" strum_macros = "0.28.0"
livekit-api = { version = "0.5.0", features = ["native-tls"] } livekit-api = { version = "0.5.6", features = ["native-tls"] }
livekit-protocol = "0.7.8" livekit-protocol = "0.7.10"
thiserror = "2.0.18" thiserror = "2.0.19"
hickory-resolver = "0.25.2" trust-dns-resolver = "0.23.2"
serde = "1.0.228" serde = "1.0.229"
serde_json = "1.0.149" serde_json = "1.0.150"
json = "0.12.4" json = "0.12.4"

View file

@ -109,7 +109,7 @@
rhoPort = lib.mkOption { rhoPort = lib.mkOption {
type = lib.types.port; type = lib.types.port;
default = 959; default = 443;
description = "Port the Omikron server listens on for incoming QUIC connections."; description = "Port the Omikron server listens on for incoming QUIC connections.";
}; };

View file

@ -1,5 +1,4 @@
use mtp::codec::{CommunicationType, CommunicationValue, DataType, DataValue, TypeMap}; use mtp::codec::{CommunicationType, CommunicationValue, DataType, DataValue, TypeMap};
use mtp::host::{Receiver, Sender};
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
@ -10,7 +9,7 @@ use crate::anonymous_clients::anonymous_manager::{self, generate_username};
use crate::calls::{call_group::call_invite_secret_from_cv, call_manager}; use crate::calls::{call_group::call_invite_secret_from_cv, call_manager};
use crate::data::user::UserStatus; use crate::data::user::UserStatus;
use crate::omega::omega_connection::{OmegaConnection, get_omega_connection}; use crate::omega::omega_connection::{OmegaConnection, get_omega_connection};
use crate::rho::connection::GeneralConnection; use crate::rho::connection::{GeneralConnection, MtpReceiver, MtpSender};
use crate::rho::rho_manager; use crate::rho::rho_manager;
use crate::util::logger::PrintType; use crate::util::logger::PrintType;
use crate::{log_cv_in, log_cv_out, log_out}; use crate::{log_cv_in, log_cv_out, log_out};
@ -18,8 +17,8 @@ use crate::{log_cv_in, log_cv_out, log_out};
pub struct AnonymousClientConnection { pub struct AnonymousClientConnection {
user_id: u64, user_id: u64,
pub sender: Arc<Sender>, pub sender: Arc<MtpSender>,
pub receiver: Arc<Receiver>, pub receiver: Arc<MtpReceiver>,
pub ping: Arc<RwLock<i64>>, pub ping: Arc<RwLock<i64>>,
pub interested_users: Arc<RwLock<Vec<i64>>>, pub interested_users: Arc<RwLock<Vec<i64>>>,
is_open: Arc<RwLock<bool>>, is_open: Arc<RwLock<bool>>,

View file

@ -16,7 +16,7 @@ pub static WORKING_DIR: Lazy<PathBuf> =
use rustls::crypto::aws_lc_rs::default_provider; use rustls::crypto::aws_lc_rs::default_provider;
use mtp::crypto::Keyring; use mtp::crypto::Keyring;
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 crate::{ use crate::{
calls::call_util::garbage_collect_calls, omega::omega_connection::get_omega_connection, calls::call_util::garbage_collect_calls, omega::omega_connection::get_omega_connection,
@ -27,9 +27,9 @@ const KEYRING_PATH: &str = "./omikron.mk";
const PUBLIC_KEY_PATH: &str = "./omikron.mpkb"; const PUBLIC_KEY_PATH: &str = "./omikron.mpkb";
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(), PUBLIC_KEY_PATH) save_public_key_bundle(&kr.public_key_bundle(), PUBLIC_KEY_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);
@ -56,7 +56,7 @@ async fn main() {
let rho_port = env::var("RHO_PORT") let rho_port = env::var("RHO_PORT")
.ok() .ok()
.and_then(|s| s.parse().ok()) .and_then(|s| s.parse().ok())
.unwrap_or(959); .unwrap_or(443);
get_omega_connection(); get_omega_connection();
tokio::spawn(async move { tokio::spawn(async move {

View file

@ -263,22 +263,21 @@ impl OmegaConnection {
.parse::<u64>() .parse::<u64>()
.unwrap_or(0), .unwrap_or(0),
) )
.with_policy(Policy { .with_policy(
send_mode: SendMode::SingleStreamPerMessage, Policy::default()
max_message_size: 1_000_000_000, .with_send_mode(SendMode::SingleStreamPerMessage)
close_frame_len: u32::MAX, .with_max_message_size(1_000_000_000)
application_close_code: 0, .with_timeouts(
open_stream_timeout: Duration::from_millis(2_000), Duration::from_millis(2_000),
write_timeout: Duration::from_millis(2_000), Duration::from_millis(2_000),
accept_stream_timeout: Duration::from_millis(10_000), Duration::from_millis(30_000),
read_timeout: Duration::from_millis(30_000), )
keep_alive_interval: Some(Duration::from_secs(6)), .with_keep_alive(Some(Duration::from_secs(6)))
max_idle_timeout: Some(Duration::from_secs(30)), .with_max_idle_timeout(Some(Duration::from_secs(30)))
force_close_delay: Duration::from_millis(300), .with_receiver_queue_capacity(1000)
max_transient_recv_errors: 20, .with_max_concurrent_stream_tasks(10)
transient_recv_backoff: Duration::from_millis(100), .with_persistent_stream_retries(5, Duration::from_secs(5)),
receiver_queue_capacity: 1000, );
});
let host_public_key = load_public_key_bundle("./omega.mpkb") let host_public_key = load_public_key_bundle("./omega.mpkb")
.map_err(|e| format!("Failed to load omega.mpkb: {}", e))?; .map_err(|e| format!("Failed to load omega.mpkb: {}", e))?;

View file

@ -1,11 +1,10 @@
use crate::anonymous_clients::anonymous_manager; use crate::anonymous_clients::anonymous_manager;
use crate::omega::omega_connection::get_omega_connection; use crate::omega::omega_connection::get_omega_connection;
use crate::rho::connection::GeneralConnection; use crate::rho::connection::{GeneralConnection, MtpReceiver, MtpSender};
use crate::rho::{rho_connection::RhoConnection, rho_manager}; use crate::rho::{rho_connection::RhoConnection, rho_manager};
use crate::util::logger::PrintType; use crate::util::logger::PrintType;
use crate::{log_cv_in, log_cv_out, log_err, log_in, log_out}; use crate::{log_cv_in, log_cv_out, log_err, log_in, log_out};
use mtp::codec::{CommunicationType, CommunicationValue, DataType, DataValue}; use mtp::codec::{CommunicationType, CommunicationValue, DataType, DataValue};
use mtp::host::{Receiver, Sender};
use std::sync::Arc; use std::sync::Arc;
use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -17,8 +16,8 @@ pub struct AppConnection {
pub app_session: Uuid, pub app_session: Uuid,
pub client_version: String, pub client_version: String,
pub sender: Arc<Sender>, pub sender: Arc<MtpSender>,
pub receiver: Arc<Receiver>, pub receiver: Arc<MtpReceiver>,
pub ping: Arc<RwLock<i64>>, pub ping: Arc<RwLock<i64>>,
pub_key: Arc<RwLock<Option<Vec<u8>>>>, pub_key: Arc<RwLock<Option<Vec<u8>>>>,
pub rho_connection: Arc<RwLock<Option<Arc<RhoConnection>>>>, pub rho_connection: Arc<RwLock<Option<Arc<RhoConnection>>>>,

View file

@ -1,17 +1,17 @@
use crate::anonymous_clients::anonymous_manager; use crate::anonymous_clients::anonymous_manager;
use crate::calls::{call_group::call_invite_secret_from_cv, call_manager, call_util}; use crate::calls::{call_group::call_invite_secret_from_cv, call_manager, call_util};
use crate::omega::omega_connection::get_omega_connection; use crate::omega::omega_connection::get_omega_connection;
use crate::rho::connection::GeneralConnection; use crate::rho::connection::{GeneralConnection, MtpReceiver, MtpSender};
use crate::rho::{rho_connection::RhoConnection, rho_manager}; use crate::rho::{rho_connection::RhoConnection, rho_manager};
use crate::util::logger::PrintType; use crate::util::logger::PrintType;
use crate::{data::user::UserStatus, omega::omega_connection::OmegaConnection}; use crate::{data::user::UserStatus, omega::omega_connection::OmegaConnection};
use crate::{log_cv_in, log_cv_out, log_err, log_in, log_out}; use crate::{log_cv_in, log_cv_out, log_err, log_in, log_out};
use mtp::codec::{CommunicationType, CommunicationValue, DataType, DataValue}; use mtp::codec::{CommunicationType, CommunicationValue, DataType, DataValue};
use mtp::host::{Receiver, Sender};
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tokio::sync::RwLock; use tokio::sync::RwLock;
use trust_dns_resolver::TokioAsyncResolver;
use uuid::Uuid; use uuid::Uuid;
pub struct ClientConnection { pub struct ClientConnection {
@ -19,8 +19,8 @@ pub struct ClientConnection {
pub session_id: u64, pub session_id: u64,
pub client_version: String, pub client_version: String,
pub sender: Arc<Sender>, pub sender: Arc<MtpSender>,
pub receiver: Arc<Receiver>, pub receiver: Arc<MtpReceiver>,
pub ping: Arc<RwLock<i64>>, pub ping: Arc<RwLock<i64>>,
pub_key: Arc<RwLock<Option<Vec<u8>>>>, pub_key: Arc<RwLock<Option<Vec<u8>>>>,
pub rho_connection: Arc<RwLock<Option<Arc<RhoConnection>>>>, pub rho_connection: Arc<RwLock<Option<Arc<RhoConnection>>>>,
@ -611,27 +611,52 @@ impl ClientConnection {
async fn handle_load_txt_record(self: Arc<Self>, cv: CommunicationValue) { async fn handle_load_txt_record(self: Arc<Self>, cv: CommunicationValue) {
if let Some(path) = cv.get_data(DataType::Path).as_str() { if let Some(path) = cv.get_data(DataType::Path).as_str() {
if let Ok(builder) = hickory_resolver::Resolver::builder_tokio() { let resolver = match TokioAsyncResolver::tokio_from_system_conf() {
let resolver = builder.build(); Ok(r) => r,
if let Ok(lookup) = resolver.txt_lookup(path).await { Err(_) => {
for record in lookup.iter() { let path_data = cv.get_data(DataType::Path).clone();
for txt_data in record.txt_data() { let error_cv = CommunicationValue::new(CommunicationType::ErrorNotFound)
if let Ok(s) = std::str::from_utf8(txt_data) {
let response =
CommunicationValue::new(CommunicationType::LoadTxtRecord)
.with_id(cv.get_id()) .with_id(cv.get_id())
.add_typed_default( .add_typed_default(DataType::Path, path_data);
DataType::Content, self.send_message(&error_cv).await;
DataValue::Str(s.to_string()), return;
); }
};
match resolver.txt_lookup(path).await {
Ok(txt_lookup) => {
if let Some(txt_record) = txt_lookup.iter().next() {
let record_text: String = txt_record
.txt_data()
.iter()
.map(|b| String::from_utf8_lossy(b))
.collect();
let response = CommunicationValue::new(CommunicationType::LoadTxtRecord)
.with_id(cv.get_id())
.add_typed_default(DataType::Content, DataValue::Str(record_text));
self.send_message(&response).await; self.send_message(&response).await;
return; return;
} }
let path_data = cv.get_data(DataType::Path).clone();
let error_cv = CommunicationValue::new(CommunicationType::ErrorNotFound)
.with_id(cv.get_id())
.add_typed_default(DataType::Path, path_data);
self.send_message(&error_cv).await;
}
Err(_) => {
let path_data = cv.get_data(DataType::Path).clone();
let error_cv = CommunicationValue::new(CommunicationType::ErrorNotFound)
.with_id(cv.get_id())
.add_typed_default(DataType::Path, path_data);
self.send_message(&error_cv).await;
} }
} }
return;
} }
}
}
let path_data = cv.get_data(DataType::Path).clone(); let path_data = cv.get_data(DataType::Path).clone();
let error_cv = CommunicationValue::new(CommunicationType::ErrorNotFound) let error_cv = CommunicationValue::new(CommunicationType::ErrorNotFound)
.with_id(cv.get_id()) .with_id(cv.get_id())

View file

@ -14,7 +14,11 @@ use crate::{
util::logger::PrintType, util::logger::PrintType,
}; };
use mtp::codec::{CommunicationType, CommunicationValue, DataType, DataTypeId, DataValue}; use mtp::codec::{CommunicationType, CommunicationValue, DataType, DataTypeId, DataValue};
use mtp::host::{AuthState, Connection as MTPHostConnection, Receiver, Sender}; use mtp::host::AuthState;
use mtp::webserver::{WebMTPConnection, WebMtpReceiver, WebMtpSender};
pub type MtpSender = WebMtpSender;
pub type MtpReceiver = WebMtpReceiver;
/* /*
* How a connection identified itself during the mtp handshake driven by * How a connection identified itself during the mtp handshake driven by
@ -34,8 +38,8 @@ pub enum ConnectionKind {
} }
pub struct GeneralConnection { pub struct GeneralConnection {
pub sender: Arc<Sender>, pub sender: Arc<MtpSender>,
pub receiver: Arc<Receiver>, pub receiver: Arc<MtpReceiver>,
connection_kind: ConnectionKind, connection_kind: ConnectionKind,
id: u64, id: u64,
@ -50,14 +54,14 @@ pub struct GeneralConnection {
impl GeneralConnection { impl GeneralConnection {
/* /*
* `conn` has already been authenticated (or deliberately left * `conn` has already been authenticated (or deliberately left
* unauthenticated) by `mtp::host::Host::accept`, via the * unauthenticated) by `mtp::webserver::MTPWebServer::accept`, via the
* `get_by_connector_id`/`complete_register` callbacks in `server.rs` * `get_by_connector_id`/`complete_register` callbacks in `server.rs`
* keyed off `conn.description`. There is no separate application-level * keyed off `conn.description`. There is no separate application-level
* challenge step anymore; a connection whose description doesn't resolve * challenge step anymore; a connection whose description doesn't resolve
* to a known, appropriately-authenticated kind is rejected here instead * to a known, appropriately-authenticated kind is rejected here instead
* of being handed off to a connection handler. * of being handed off to a connection handler.
*/ */
pub fn new(conn: MTPHostConnection) -> Option<Arc<Self>> { pub fn new(conn: WebMTPConnection) -> Option<Arc<Self>> {
let kind = match (conn.description.as_deref(), &conn.auth_state) { let kind = match (conn.description.as_deref(), &conn.auth_state) {
(Some("iota"), AuthState::Authenticated) => ConnectionKind::Iota, (Some("iota"), AuthState::Authenticated) => ConnectionKind::Iota,
(Some("client"), AuthState::Authenticated) => ConnectionKind::Client, (Some("client"), AuthState::Authenticated) => ConnectionKind::Client,

View file

@ -6,7 +6,7 @@ use crate::log_err;
use crate::log_in; use crate::log_in;
use crate::log_out; use crate::log_out;
use crate::omega::omega_connection::get_omega_connection; use crate::omega::omega_connection::get_omega_connection;
use crate::rho::connection::GeneralConnection; use crate::rho::connection::{GeneralConnection, MtpReceiver, MtpSender};
use crate::util::logger::PrintType; use crate::util::logger::PrintType;
use dashmap::DashMap; use dashmap::DashMap;
use mtp::codec::CommunicationType; use mtp::codec::CommunicationType;
@ -16,8 +16,6 @@ use mtp::codec::DataTypeId;
use mtp::codec::DataValue; use mtp::codec::DataValue;
use mtp::codec::TypeMap; use mtp::codec::TypeMap;
use mtp::crypto::KemPublicKey; use mtp::crypto::KemPublicKey;
use mtp::host::Receiver;
use mtp::host::Sender;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::{collections::HashMap, sync::Arc, sync::LazyLock, time::Duration}; use std::{collections::HashMap, sync::Arc, sync::LazyLock, time::Duration};
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -33,8 +31,8 @@ static PENDING_CHAT_SECRETS: LazyLock<DashMap<u64, Vec<CommunicationValue>>> =
pub struct IotaConnection { pub struct IotaConnection {
pub iota_id: u64, pub iota_id: u64,
pub client_version: String, pub client_version: String,
pub sender: Arc<Sender>, pub sender: Arc<MtpSender>,
pub receiver: Arc<Receiver>, pub receiver: Arc<MtpReceiver>,
pub user_ids: Arc<RwLock<Vec<u64>>>, pub user_ids: Arc<RwLock<Vec<u64>>>,
pub ping: Arc<RwLock<i64>>, pub ping: Arc<RwLock<i64>>,
pub_key: Arc<RwLock<Option<Vec<u8>>>>, pub_key: Arc<RwLock<Option<Vec<u8>>>>,

View file

@ -13,7 +13,8 @@ use crate::{
}; };
use mtp::codec::{CommunicationType, CommunicationValue, DataType, DataValue}; use mtp::codec::{CommunicationType, CommunicationValue, DataType, DataValue};
use mtp::crypto::PublicKeyBundle; use mtp::crypto::PublicKeyBundle;
use mtp::host::{AuthenticationPolicy, Host, HostConfig, Policy, SendMode}; use mtp::host::{AuthenticationPolicy, HostConfig, Policy, SendMode};
use mtp::webserver::{MTPWebServer, WebServerConfig};
/* /*
* Resolves the PublicKeyBundle mtp needs to verify a login's signed * Resolves the PublicKeyBundle mtp needs to verify a login's signed
@ -107,22 +108,21 @@ pub async fn start(port: u16) -> Result<(), Box<dyn std::error::Error>> {
cert_pem, cert_pem,
key_pem, key_pem,
) )
.with_policy(Policy { .with_policy(
send_mode: SendMode::SingleStreamPerMessage, Policy::default()
max_message_size: 1_000_000_000, .with_send_mode(SendMode::SingleStreamPerMessage)
close_frame_len: u32::MAX, .with_max_message_size(1_000_000_000)
application_close_code: 0, .with_timeouts(
open_stream_timeout: Duration::from_millis(2_000), Duration::from_millis(2_000),
write_timeout: Duration::from_millis(2_000), Duration::from_millis(2_000),
accept_stream_timeout: Duration::from_millis(10_000), Duration::from_millis(30_000),
read_timeout: Duration::from_millis(30_000), )
keep_alive_interval: Some(Duration::from_secs(6)), .with_keep_alive(Some(Duration::from_secs(6)))
max_idle_timeout: Some(Duration::from_secs(30)), .with_max_idle_timeout(Some(Duration::from_secs(30)))
force_close_delay: Duration::from_millis(300), .with_receiver_queue_capacity(1000)
max_transient_recv_errors: 20, .with_max_concurrent_stream_tasks(10)
transient_recv_backoff: Duration::from_millis(100), .with_persistent_stream_retries(5, Duration::from_secs(5)),
receiver_queue_capacity: 1000, )
})
.with_authentication( .with_authentication(
load_keyring(), load_keyring(),
Box::new(|user_id, description| Box::pin(get_by_connector_id(user_id, description))), Box::new(|user_id, description| Box::pin(get_by_connector_id(user_id, description))),
@ -130,7 +130,9 @@ pub async fn start(port: u16) -> Result<(), Box<dyn std::error::Error>> {
) )
.with_authentication_policy(AuthenticationPolicy::AllowAuthentication); .with_authentication_policy(AuthenticationPolicy::AllowAuthentication);
let mut host: Host = Host::new(host_config).await?; let web_config = WebServerConfig::new()
.route("/", |_request, response| async move { response.body("OK") })?;
let mut host = MTPWebServer::new(host_config, web_config).await?;
log!(0, PrintType::General, "Server listening on port {}.", port); log!(0, PrintType::General, "Server listening on port {}.", port);
loop { loop {

View file

@ -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