From c6b83b3ad9e24e80d91e59d9136caea2bd9b2e63 Mon Sep 17 00:00:00 2001 From: DJMrTV Date: Sat, 8 Feb 2025 11:54:49 +0100 Subject: [PATCH 01/14] feat(matchmake): add create_matchmake_session_with_param and change some/add other stuff --- src/kerberos/mod.rs | 2 +- src/protocols/matchmake_common/mod.rs | 97 ++++++++++++++++--- ...thod_auto_matchmake_with_param_postpone.rs | 42 +++++--- ...hod_create_matchmake_session_with_param.rs | 62 ++++++++++++ src/protocols/matchmake_extension/mod.rs | 3 + src/protocols/notification/mod.rs | 6 +- src/prudp/socket.rs | 21 +++- src/rmc/structures/matchmake.rs | 20 +++- src/rmc/structures/variant.rs | 3 +- 9 files changed, 220 insertions(+), 36 deletions(-) create mode 100644 src/protocols/matchmake_extension/method_create_matchmake_session_with_param.rs diff --git a/src/kerberos/mod.rs b/src/kerberos/mod.rs index 8fb46fa..5fafd9e 100644 --- a/src/kerberos/mod.rs +++ b/src/kerberos/mod.rs @@ -25,7 +25,7 @@ pub fn derive_key(pid: u32, password: [u8; 16]) -> [u8; 16]{ key } -#[derive(Pod, Zeroable, Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Pod, Zeroable, Copy, Clone, Debug, Eq, PartialEq, Default)] #[repr(transparent)] pub struct KerberosDateTime(pub u64); diff --git a/src/protocols/matchmake_common/mod.rs b/src/protocols/matchmake_common/mod.rs index 79f7c97..ab904f7 100644 --- a/src/protocols/matchmake_common/mod.rs +++ b/src/protocols/matchmake_common/mod.rs @@ -1,11 +1,15 @@ use std::collections::{BTreeMap}; use std::sync::Arc; use log::error; -use tokio::sync::Mutex; +use rand::random; +use tokio::sync::{Mutex, RwLock}; +use crate::kerberos::KerberosDateTime; use crate::protocols::notification::Notification; -use crate::prudp::socket::{ConnectionData, SocketData}; -use crate::rmc::structures::matchmake::MatchmakeSession; +use crate::prudp::socket::{ActiveConnectionData, ConnectionData, SocketData}; +use crate::rmc::structures::matchmake::{Gathering, MatchmakeParam, MatchmakeSession}; +use crate::rmc::structures::variant::Variant; +#[derive(Default, Debug)] pub struct ExtendedMatchmakeSession{ pub session: MatchmakeSession, pub connected_players: Vec>>, @@ -16,22 +20,75 @@ pub struct MatchmakeData{ } impl ExtendedMatchmakeSession{ + pub async fn from_matchmake_session(session: MatchmakeSession, host: &Mutex) -> Self{ + let host = host.lock().await; + + let ConnectionData{ + active_connection_data, + .. + } = &*host; + + let Some(active_connection_data) = active_connection_data else{ + return Default::default(); + }; + + let ActiveConnectionData{ + active_secure_connection_data, + .. + } = active_connection_data; + + let Some(active_secure_connection_data) = active_secure_connection_data else{ + return Default::default(); + }; + + + let mm_session = MatchmakeSession{ + gathering: Gathering{ + self_gid: random(), + owner_pid: active_secure_connection_data.pid, + host_pid: active_secure_connection_data.pid, + ..session.gathering.clone() + }, + datetime: KerberosDateTime::now(), + session_key: (0..32).map(|_| random()).collect(), + matchmake_param: MatchmakeParam{ + params: vec![ + ("@SR".to_owned(), Variant::Bool(true)), + ("@GIR".to_owned(), Variant::SInt64(3)) + ] + }, + ..session + }; + + Self{ + session: mm_session, + connected_players: Default::default() + } + } + pub async fn add_player(&mut self, socket: &SocketData, conn: Arc>, join_msg: String) { - let Some(pid) = conn.lock().await.active_connection_data.as_ref() + let locked = conn.lock().await; + + let Some(joining_pid) = locked.active_connection_data.as_ref() .map(|c| c.active_secure_connection_data.as_ref() - .map(|c| c.pid - ) + .map(|c| c.pid) ).flatten() else { error!("tried to add player without secure connection"); return }; + drop(locked); + self.connected_players.push(conn); + self.session.participation_count = self.connected_players.len() as u32; - for conn in &self.connected_players{ - let Some(other_pid) = conn.lock().await.active_connection_data.as_ref() + for other_connection in &self.connected_players{ + let mut conn = other_connection.lock().await; + + + let Some(other_pid) = conn.active_connection_data.as_ref() .map(|c| c.active_secure_connection_data.as_ref() .map(|c| c.pid @@ -41,22 +98,38 @@ impl ExtendedMatchmakeSession{ return }; - let mut conn = conn.lock().await; + /*if other_pid == self.session.gathering.owner_pid && + joining_pid == self.session.gathering.owner_pid{ + continue; + }*/ conn.send_notification(socket, Notification{ - pid_source: pid, + pid_source: joining_pid, notif_type: 3001, param_1: self.session.gathering.self_gid, param_2: other_pid, str_param: join_msg.clone(), + param_3: self.session.participation_count }).await; - - } } } +pub async fn add_matchmake_session(mm_data: Arc>,session: ExtendedMatchmakeSession) -> Arc> { + let gid = session.session.gathering.self_gid; + + let mut mm_data = mm_data.write().await; + + let session = Arc::new(Mutex::new(session)); + + mm_data.matchmake_sessions.insert(gid, session.clone()); + + session +} impl MatchmakeData { + + + pub async fn try_find_session_with_criteria(&self, ) -> Option>>{ None } diff --git a/src/protocols/matchmake_extension/method_auto_matchmake_with_param_postpone.rs b/src/protocols/matchmake_extension/method_auto_matchmake_with_param_postpone.rs index 2d53080..c822dc0 100644 --- a/src/protocols/matchmake_extension/method_auto_matchmake_with_param_postpone.rs +++ b/src/protocols/matchmake_extension/method_auto_matchmake_with_param_postpone.rs @@ -1,12 +1,16 @@ use std::io::Cursor; use std::sync::Arc; +use std::time::Duration; +use chrono::SecondsFormat::Millis; +use log::info; use rand::random; use tokio::sync::{Mutex, RwLock}; +use tokio::time::sleep; use crate::protocols::matchmake_common::{ExtendedMatchmakeSession, MatchmakeData}; use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponseResult}; -use crate::rmc::structures::matchmake::{AutoMatchmakeParam}; +use crate::rmc::structures::matchmake::{AutoMatchmakeParam, MatchmakeSession}; use crate::rmc::structures::RmcSerialize; @@ -18,7 +22,7 @@ pub async fn auto_matchmake_with_param_postpone( mm_data: Arc>, auto_matchmake_param: AutoMatchmakeParam ) -> RMCResponseResult{ - println!("auto_matchmake_with_param_postpone: {:?}", auto_matchmake_param); + //println!("auto_matchmake_with_param_postpone: {:?}", auto_matchmake_param); let locked_conn = conn.lock().await; let Some(secure_conn) = locked_conn.active_connection_data.as_ref().map(|a| a.active_secure_connection_data.as_ref()).flatten() else { @@ -38,35 +42,41 @@ pub async fn auto_matchmake_with_param_postpone( // up anything else unnescesarily drop(mm_data_read); - let gid = random(); - - let mut matchmake_session = auto_matchmake_param.matchmake_session.clone(); - matchmake_session.gathering.self_gid = gid; - matchmake_session.gathering.host_pid = pid; - matchmake_session.gathering.owner_pid = pid; - + let session = + ExtendedMatchmakeSession::from_matchmake_session(auto_matchmake_param.matchmake_session, &conn).await; + let gid = session.session.gathering.self_gid; let mut mm_data = mm_data.write().await; - let session = Arc::new(Mutex::new(ExtendedMatchmakeSession{ - session: matchmake_session.clone(), - connected_players: Vec::new() - })); + let session = Arc::new(Mutex::new(session)); mm_data.matchmake_sessions.insert(gid, session.clone()); session }; - let mut session = session.lock().await; + let mut locked_session = session.lock().await; //todo: refactor so that this works - session.add_player(socket, conn.clone(), auto_matchmake_param.join_message).await; + { + let session = session.clone(); + let socket = socket.clone(); + let connection = conn.clone(); + let join_msg = auto_matchmake_param.join_message.clone(); + tokio::spawn(async move{ + sleep(Duration::from_millis(500)).await; + println!("adding player"); + let mut session = session.lock().await; + session.add_player(&socket, connection, join_msg).await; + }); + } + + info!("new session: {:?}", locked_session); let mut response = Vec::new(); - session.session.serialize(&mut response).expect("unable to serialize matchmake session"); + locked_session.session.serialize(&mut response).expect("unable to serialize matchmake session"); rmcmessage.success_with_data(response) } diff --git a/src/protocols/matchmake_extension/method_create_matchmake_session_with_param.rs b/src/protocols/matchmake_extension/method_create_matchmake_session_with_param.rs new file mode 100644 index 0000000..fd31c55 --- /dev/null +++ b/src/protocols/matchmake_extension/method_create_matchmake_session_with_param.rs @@ -0,0 +1,62 @@ +use std::io::Cursor; +use std::sync::Arc; +use tokio::sync::{Mutex, RwLock}; +use crate::protocols::matchmake_common::{add_matchmake_session, ExtendedMatchmakeSession, MatchmakeData}; +use crate::protocols::matchmake_extension::method_auto_matchmake_with_param_postpone::auto_matchmake_with_param_postpone; +use crate::prudp::socket::{ConnectionData, SocketData}; +use crate::rmc::message::RMCMessage; +use crate::rmc::response::{ErrorCode, RMCResponseResult}; +use crate::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam}; +use crate::rmc::structures::RmcSerialize; + +pub async fn create_matchmake_session_with_param( + rmcmessage: &RMCMessage, + conn: &Arc>, + socket: &Arc, + mm_data: Arc>, + create_matchmake_session: CreateMatchmakeSessionParam +) -> RMCResponseResult { + let locked_conn = conn.lock().await; + let Some(secure_conn) = + locked_conn.active_connection_data.as_ref().map(|a| a.active_secure_connection_data.as_ref()).flatten() else { + return rmcmessage.error_result_with_code(ErrorCode::Core_Exception); + }; + + println!("{:?}", create_matchmake_session); + + let pid = secure_conn.pid; + + let mut session = + ExtendedMatchmakeSession::from_matchmake_session(create_matchmake_session.matchmake_session, &conn).await; + + session.session.participation_count = create_matchmake_session.participation_count as u32; + + let session = add_matchmake_session(mm_data, session).await; + + let mut session = session.lock().await; + + session.add_player(&socket, conn.clone(), create_matchmake_session.join_message).await; + + let mut response = Vec::new(); + + session.session.serialize(&mut response).expect("unable to serialize session"); + + rmcmessage.success_with_data(response) +} + +pub async fn create_matchmake_session_with_param_raw_params( + rmcmessage: &RMCMessage, + socket: &Arc, + connection_data: &Arc>, + data: Arc> +) -> RMCResponseResult{ + let mut reader = Cursor::new(&rmcmessage.rest_of_data); + + let Ok(matchmake_param) = CreateMatchmakeSessionParam::deserialize(&mut reader) else { + return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument); + }; + + + + create_matchmake_session_with_param(rmcmessage, connection_data, socket, data, matchmake_param).await +} \ No newline at end of file diff --git a/src/protocols/matchmake_extension/mod.rs b/src/protocols/matchmake_extension/mod.rs index 509f3bb..ddca083 100644 --- a/src/protocols/matchmake_extension/mod.rs +++ b/src/protocols/matchmake_extension/mod.rs @@ -1,5 +1,6 @@ mod method_get_playing_session; mod method_auto_matchmake_with_param_postpone; +mod method_create_matchmake_session_with_param; use std::sync::Arc; use tokio::sync::{RwLock}; @@ -7,10 +8,12 @@ use crate::define_protocol; use crate::protocols::matchmake_common::MatchmakeData; use method_get_playing_session::get_playing_session_raw_params; use method_auto_matchmake_with_param_postpone::auto_matchmake_with_param_postpone_raw_params; +use crate::protocols::matchmake_extension::method_create_matchmake_session_with_param::create_matchmake_session_with_param_raw_params; define_protocol!{ 109(matchmake_data: Arc>) => { 16 => get_playing_session_raw_params, + 38 => create_matchmake_session_with_param_raw_params, 40 => auto_matchmake_with_param_postpone_raw_params } } \ No newline at end of file diff --git a/src/protocols/notification/mod.rs b/src/protocols/notification/mod.rs index 7c96090..daa33ee 100644 --- a/src/protocols/notification/mod.rs +++ b/src/protocols/notification/mod.rs @@ -15,17 +15,19 @@ pub struct Notification{ pub param_1: u32, pub param_2: u32, pub str_param: String, + pub param_3: u32, } impl ConnectionData{ pub async fn send_notification(&mut self, socket: &SocketData, notif: Notification){ + println!("sending notification"); let mut data = Vec::new(); notif.serialize(&mut data).expect("unable to write"); let message = RMCMessage{ - protocol_id: 0xE, + protocol_id: 14, method_id: 1, call_id: random(), rest_of_data: data @@ -43,6 +45,8 @@ impl ConnectionData{ packet_signature: [0;16] }; + + self.finish_and_send_packet_to(socket, prudp_packet).await; } } \ No newline at end of file diff --git a/src/prudp/socket.rs b/src/prudp/socket.rs index ecf0108..c7b93ee 100644 --- a/src/prudp/socket.rs +++ b/src/prudp/socket.rs @@ -1,4 +1,6 @@ +use log::info; use std::collections::{HashMap, VecDeque}; +use std::fmt::{Debug, Formatter}; use std::future::Future; use std::ops::Deref; use std::pin::Pin; @@ -29,6 +31,7 @@ pub struct Socket { type OnConnectHandlerFn = Box Pin, Vec, Option)>> + Send>> + Send + Sync>; type OnDataHandlerFn = Box, Arc>) -> Pin + Send>> + Send + Sync>; +#[derive(Debug)] pub struct ActiveSecureConnectionData { pub(crate) pid: u32, pub(crate) session_key: [u8; 32], @@ -49,6 +52,13 @@ pub struct EncryptionPair{ pub recv: Box } +impl Debug for EncryptionPair{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "Stubbed") + } +} + +#[derive(Debug)] pub struct ActiveConnectionData { pub reliable_client_counter: u16, pub reliable_server_counter: u16, @@ -60,6 +70,7 @@ pub struct ActiveConnectionData { } +#[derive(Debug)] pub struct ConnectionData { pub sock_addr: PRUDPSockAddr, pub id: u64, @@ -174,8 +185,8 @@ impl SocketData { // dont keep holding the connections list unnescesarily drop(connections); + let mutual_exclusion_packet_handeling_mtx = conn.1.lock().await; let mut connection = conn.0.lock().await; - //let _mutual_exclusion_packet_handeling_mtx = conn.1.lock().await; if (packet.header.types_and_flags.get_flags() & ACK) != 0 { //todo: handle acknowledgements and resending packets propperly @@ -387,6 +398,8 @@ impl SocketData { .. } = &mut *connection; + info!("got ping"); + if (packet.header.types_and_flags.get_flags() & NEED_ACK) != 0 { let Some(active_connection) = active_connection_data.as_mut() else { error!("got data packet on non active connection!"); @@ -438,6 +451,8 @@ impl SocketData { } _ => error!("unimplemented packet type: {}", packet.header.types_and_flags.get_types()) } + + drop(mutual_exclusion_packet_handeling_mtx) } } @@ -459,6 +474,10 @@ impl ConnectionData{ encryption.apply_keystream(&mut packet.payload); } + packet.header.session_id = self.active_connection_data.as_ref().map(|v| v.server_session_id).unwrap_or_default(); + + + packet.header.source_port = socket.virtual_port; packet.header.destination_port = self.sock_addr.virtual_port; diff --git a/src/rmc/structures/matchmake.rs b/src/rmc/structures/matchmake.rs index 72cb6a0..f11b432 100644 --- a/src/rmc/structures/matchmake.rs +++ b/src/rmc/structures/matchmake.rs @@ -3,7 +3,7 @@ use crate::kerberos::KerberosDateTime; use crate::rmc::structures::variant::Variant; // rmc structure -#[derive(RmcSerialize, Debug, Clone)] +#[derive(RmcSerialize, Debug, Clone, Default)] #[rmc_struct(0)] pub struct Gathering { pub self_gid: u32, @@ -19,7 +19,7 @@ pub struct Gathering { } // rmc structure -#[derive(RmcSerialize, Debug, Clone)] +#[derive(RmcSerialize, Debug, Clone, Default)] #[rmc_struct(0)] pub struct MatchmakeParam { pub params: Vec<(String, Variant)>, @@ -27,7 +27,7 @@ pub struct MatchmakeParam { // rmc structure -#[derive(RmcSerialize, Debug, Clone)] +#[derive(RmcSerialize, Debug, Clone, Default)] #[rmc_struct(3)] pub struct MatchmakeSession { //inherits from @@ -81,4 +81,16 @@ pub struct AutoMatchmakeParam { pub participation_count: u16, pub search_criteria: Vec, pub target_gids: Vec, -} \ No newline at end of file +} + +#[derive(RmcSerialize, Debug, Clone)] +#[rmc_struct(0)] +pub struct CreateMatchmakeSessionParam { + pub matchmake_session: MatchmakeSession, + pub additional_participants: Vec, + pub gid_for_participation_check: u32, + pub create_matchmake_session_option: u32, + pub join_message: String, + pub participation_count: u16, + +} diff --git a/src/rmc/structures/variant.rs b/src/rmc/structures/variant.rs index 7472b03..ebd25b8 100644 --- a/src/rmc/structures/variant.rs +++ b/src/rmc/structures/variant.rs @@ -3,8 +3,9 @@ use crate::kerberos::KerberosDateTime; use crate::rmc::structures; use crate::rmc::structures::RmcSerialize; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub enum Variant{ + #[default] None, SInt64(i64), Double(f64), From 7d24a71f09bd1f08b90e6a3a9c17fb19d971d0ac Mon Sep 17 00:00:00 2001 From: DJMrTV Date: Wed, 12 Feb 2025 18:46:29 +0100 Subject: [PATCH 02/14] feat(matchmake): a bunch of things --- src/kerberos/mod.rs | 16 ++- src/main.rs | 9 +- src/protocols/matchmake_common/mod.rs | 1 + ...hod_create_matchmake_session_with_param.rs | 13 +- src/protocols/mod.rs | 1 + .../method_report_nat_properties.rs | 27 ++++ src/protocols/nat_traversal/mod.rs | 10 ++ src/protocols/notification/mod.rs | 136 +++++++++++++++++- src/protocols/server.rs | 4 +- src/prudp/packet.rs | 22 ++- src/prudp/socket.rs | 5 +- src/rmc/message.rs | 6 +- src/rmc/response.rs | 2 +- 13 files changed, 221 insertions(+), 31 deletions(-) create mode 100644 src/protocols/nat_traversal/method_report_nat_properties.rs create mode 100644 src/protocols/nat_traversal/mod.rs diff --git a/src/kerberos/mod.rs b/src/kerberos/mod.rs index 5fafd9e..a40b816 100644 --- a/src/kerberos/mod.rs +++ b/src/kerberos/mod.rs @@ -66,7 +66,7 @@ impl KerberosDateTime{ #[inline] pub fn get_month(&self) -> u8{ - ((self.0 >> 22) & 0b111111) as u8 + ((self.0 >> 22) & 0b1111) as u8 } #[inline] @@ -154,3 +154,17 @@ impl Ticket{ data.into_boxed_slice() } } + + +#[cfg(test)] +mod test{ + use chrono::{Datelike, Utc}; + use crate::kerberos::KerberosDateTime; + + #[test] + fn kerberos_time_convert_test(){ + let time = KerberosDateTime(135904948834); + + println!("{}", time.to_regular_time().to_rfc2822()); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index b2d7765..2b445c7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -64,11 +64,9 @@ static OWN_IP_PRIVATE: Lazy = Lazy::new(||{ .expect("no public ip specified") }); -static OWN_IP_PUBLIC: Lazy = Lazy::new(||{ +static OWN_IP_PUBLIC: Lazy = Lazy::new(||{ env::var("SERVER_IP_PUBLIC") - .ok() - .and_then(|s| s.parse().ok()) - .unwrap_or(*OWN_IP_PRIVATE) + .unwrap_or(OWN_IP_PRIVATE.to_string()) }); static SECURE_STATION_URL: Lazy = Lazy::new(|| @@ -194,7 +192,8 @@ async fn start_secure_server() -> SecureServer{ Box::new(block_if_maintenance), Box::new(protocols::secure::bound_protocol()), Box::new(protocols::matchmake::bound_protocol(matchmake_data.clone())), - Box::new(protocols::matchmake_extension::bound_protocol(matchmake_data)) + Box::new(protocols::matchmake_extension::bound_protocol(matchmake_data)), + Box::new(protocols::nat_traversal::bound_protocol()) ])); let socket = diff --git a/src/protocols/matchmake_common/mod.rs b/src/protocols/matchmake_common/mod.rs index ab904f7..f016457 100644 --- a/src/protocols/matchmake_common/mod.rs +++ b/src/protocols/matchmake_common/mod.rs @@ -57,6 +57,7 @@ impl ExtendedMatchmakeSession{ ("@GIR".to_owned(), Variant::SInt64(3)) ] }, + system_password_enabled: false, ..session }; diff --git a/src/protocols/matchmake_extension/method_create_matchmake_session_with_param.rs b/src/protocols/matchmake_extension/method_create_matchmake_session_with_param.rs index fd31c55..6fa7214 100644 --- a/src/protocols/matchmake_extension/method_create_matchmake_session_with_param.rs +++ b/src/protocols/matchmake_extension/method_create_matchmake_session_with_param.rs @@ -1,5 +1,6 @@ use std::io::Cursor; use std::sync::Arc; +use log::info; use tokio::sync::{Mutex, RwLock}; use crate::protocols::matchmake_common::{add_matchmake_session, ExtendedMatchmakeSession, MatchmakeData}; use crate::protocols::matchmake_extension::method_auto_matchmake_with_param_postpone::auto_matchmake_with_param_postpone; @@ -16,15 +17,6 @@ pub async fn create_matchmake_session_with_param( mm_data: Arc>, create_matchmake_session: CreateMatchmakeSessionParam ) -> RMCResponseResult { - let locked_conn = conn.lock().await; - let Some(secure_conn) = - locked_conn.active_connection_data.as_ref().map(|a| a.active_secure_connection_data.as_ref()).flatten() else { - return rmcmessage.error_result_with_code(ErrorCode::Core_Exception); - }; - - println!("{:?}", create_matchmake_session); - - let pid = secure_conn.pid; let mut session = ExtendedMatchmakeSession::from_matchmake_session(create_matchmake_session.matchmake_session, &conn).await; @@ -37,8 +29,11 @@ pub async fn create_matchmake_session_with_param( session.add_player(&socket, conn.clone(), create_matchmake_session.join_message).await; + println!("{:?}", session); + let mut response = Vec::new(); + session.session.serialize(&mut response).expect("unable to serialize session"); rmcmessage.success_with_data(response) diff --git a/src/protocols/mod.rs b/src/protocols/mod.rs index 89916af..51c37e1 100644 --- a/src/protocols/mod.rs +++ b/src/protocols/mod.rs @@ -18,6 +18,7 @@ pub mod matchmake_extension; pub mod matchmake_common; pub mod matchmake; mod notification; +pub mod nat_traversal; static IS_MAINTENANCE: Lazy = Lazy::new(|| { env::var("IS_MAINTENANCE") diff --git a/src/protocols/nat_traversal/method_report_nat_properties.rs b/src/protocols/nat_traversal/method_report_nat_properties.rs new file mode 100644 index 0000000..32ba30d --- /dev/null +++ b/src/protocols/nat_traversal/method_report_nat_properties.rs @@ -0,0 +1,27 @@ +use std::io::Cursor; +use std::sync::Arc; +use tokio::sync::{Mutex, RwLock}; +use crate::protocols::matchmake_common::MatchmakeData; +use crate::prudp::socket::{ConnectionData, SocketData}; +use crate::rmc::message::RMCMessage; +use crate::rmc::response::{ErrorCode, RMCResponseResult}; +use crate::rmc::structures::matchmake::CreateMatchmakeSessionParam; + +pub async fn report_nat_properties( + rmcmessage: &RMCMessage, + socket: &Arc, + connection_data: &Arc>, +) -> RMCResponseResult{ + rmcmessage.success_with_data(Vec::new()) +} + +pub async fn report_nat_properties_raw_params( + rmcmessage: &RMCMessage, + socket: &Arc, + connection_data: &Arc>, + _: () +) -> RMCResponseResult{ + let mut reader = Cursor::new(&rmcmessage.rest_of_data); + + report_nat_properties(rmcmessage, socket, connection_data).await +} \ No newline at end of file diff --git a/src/protocols/nat_traversal/mod.rs b/src/protocols/nat_traversal/mod.rs new file mode 100644 index 0000000..48e59e6 --- /dev/null +++ b/src/protocols/nat_traversal/mod.rs @@ -0,0 +1,10 @@ +mod method_report_nat_properties; + +use crate::define_protocol; +use crate::protocols::nat_traversal::method_report_nat_properties::report_nat_properties_raw_params; + +define_protocol!{ + 3() => { + 5 => report_nat_properties_raw_params + } +} \ No newline at end of file diff --git a/src/protocols/notification/mod.rs b/src/protocols/notification/mod.rs index daa33ee..87340a7 100644 --- a/src/protocols/notification/mod.rs +++ b/src/protocols/notification/mod.rs @@ -1,13 +1,13 @@ use macros::RmcSerialize; use rand::random; -use crate::prudp::packet::{PRUDPHeader, PRUDPPacket, TypesFlags}; +use crate::prudp::packet::{PRUDPHeader, PRUDPPacket, PacketOption, TypesFlags}; use crate::prudp::packet::flags::{NEED_ACK, RELIABLE}; use crate::prudp::packet::types::DATA; use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::structures::RmcSerialize; -#[derive(RmcSerialize)] +#[derive(Debug, Eq, PartialEq, RmcSerialize)] #[rmc_struct(0)] pub struct Notification{ pub pid_source: u32, @@ -18,6 +18,7 @@ pub struct Notification{ pub param_3: u32, } + impl ConnectionData{ pub async fn send_notification(&mut self, socket: &SocketData, notif: Notification){ println!("sending notification"); @@ -33,20 +34,143 @@ impl ConnectionData{ rest_of_data: data }; - let prudp_packet = PRUDPPacket{ + + + let mut prudp_packet = PRUDPPacket{ header: PRUDPHeader{ types_and_flags: TypesFlags::default().types(DATA).flags(NEED_ACK | RELIABLE), source_port: socket.get_virual_port(), destination_port: self.sock_addr.virtual_port, ..Default::default() }, - options: Vec::new(), + options: vec![ + PacketOption::FragmentId(0), + ], payload: message.to_data(), packet_signature: [0;16] }; - - self.finish_and_send_packet_to(socket, prudp_packet).await; + } +} + +#[cfg(test)] +mod test{ + use std::io::Cursor; + use rand::random; + use crate::protocols::notification::Notification; + use crate::prudp::packet::{PRUDPHeader, PRUDPPacket, PacketOption, TypesFlags}; + use crate::prudp::packet::flags::{NEED_ACK, RELIABLE}; + use crate::prudp::packet::types::DATA; + use crate::rmc::message::RMCMessage; + use crate::rmc::structures::RmcSerialize; + + #[test] + fn test(){ + let data = hex::decode("ead001032900a1af62000000000000000000000000000000000000000000020100250000000e57238a6601000000001700000051b39957b90b00003661636851b3995701000001000000").unwrap(); + + + let packet = PRUDPPacket::new(&mut Cursor::new(data)).expect("invalid packet"); + + println!("{:?}", packet); + + let rmc = RMCMessage::new(&mut Cursor::new(packet.payload)).expect("invalid rmc message"); + + println!("{:?}", rmc); + + let notif = Notification::deserialize(&mut Cursor::new(rmc.rest_of_data)).expect("invalid notification"); + + println!("{:?}", notif); + } + #[test] + fn test2(){ + + let data = hex::decode("250000000e57b6801001000000001700000051b39957b90b0000248a5a9851b3995701000001000000").unwrap(); + //let packet = PRUDPPacket::new(&mut Cursor::new(data)).expect("invalid packet"); + + //println!("{:?}", packet); + + let rmc = RMCMessage::new(&mut Cursor::new(data)).expect("invalid rmc message"); + + println!("{:?}", rmc); + + let notif = Notification::deserialize(&mut Cursor::new(rmc.rest_of_data)).expect("invalid notification"); + + println!("{:?}", notif); + } + + #[test] + fn test3(){ + + let data = hex::decode("ead001032900a1af620084000300020100250000008e0100000001000000001700000051b39957b90b00000100000051b399570100000100000000000000000000000000000000000000").unwrap(); + let packet = PRUDPPacket::new(&mut Cursor::new(data)).expect("invalid packet"); + + println!("{:?}", packet); + + let rmc = RMCMessage::new(&mut Cursor::new(packet.payload)).expect("invalid rmc message"); + + println!("{:?}", rmc); + + let notif = Notification::deserialize(&mut Cursor::new(rmc.rest_of_data)).expect("invalid notification"); + + println!("{:?}", notif); + } + + #[test] + fn test_rmc_serialization(){ + let notif = Notification{ + pid_source: random(), + notif_type: random(), + param_1: random(), + param_2: random(), + str_param: "".to_string(), + param_3: random(), + }; + + let mut notif_data = Vec::new(); + + notif.serialize(&mut notif_data).unwrap(); + + let message = RMCMessage{ + protocol_id: 14, + method_id: 1, + call_id: random(), + rest_of_data: notif_data + }; + + let mut prudp_packet = PRUDPPacket{ + header: PRUDPHeader{ + ..Default::default() + }, + options: vec![ + PacketOption::FragmentId(0), + ], + payload: message.to_data(), + packet_signature: [0;16] + }; + + prudp_packet.set_sizes(); + + + + let mut packet_data: Vec = Vec::new(); + + prudp_packet.write_to(&mut packet_data).expect("what"); + + let packet_deserialized = PRUDPPacket::new(&mut Cursor::new(packet_data)).unwrap(); + + assert_eq!(prudp_packet, packet_deserialized); + + let message_deserialized = RMCMessage::new(&mut Cursor::new(packet_deserialized.payload)).unwrap(); + + assert_eq!(message, message_deserialized); + + let notification_deserialized = Notification::deserialize(&mut Cursor::new(message_deserialized.rest_of_data)).unwrap(); + + assert_eq!(notification_deserialized, notif); + + + + } } \ No newline at end of file diff --git a/src/protocols/server.rs b/src/protocols/server.rs index 9927937..68fd7c2 100644 --- a/src/protocols/server.rs +++ b/src/protocols/server.rs @@ -29,7 +29,9 @@ impl RMCProtocolServer{ for proto in &self.0 { if let Some(response) = proto(&rmc, &socket, &connection).await { - + if matches!(response.response_result, RMCResponseResult::Error {..}){ + error!("an rmc error occurred") + } let mut locked = connection.lock().await; send_response(&packet, &socket, &mut locked, response).await; drop(locked); diff --git a/src/prudp/packet.rs b/src/prudp/packet.rs index 434d476..470f6b2 100644 --- a/src/prudp/packet.rs +++ b/src/prudp/packet.rs @@ -40,7 +40,7 @@ pub enum Error { pub type Result = std::result::Result; #[repr(transparent)] -#[derive(Copy, Clone, Pod, Zeroable, SwapEndian, Default)] +#[derive(Copy, Clone, Pod, Zeroable, SwapEndian, Default, Eq, PartialEq)] pub struct TypesFlags(u16); impl TypesFlags { @@ -141,7 +141,7 @@ impl Debug for VirtualPort { } #[repr(C)] -#[derive(Debug, Copy, Clone, Pod, Zeroable, SwapEndian)] +#[derive(Debug, Copy, Clone, Pod, Zeroable, SwapEndian, Eq, PartialEq)] pub struct PRUDPHeader { pub magic: [u8; 2], pub version: u8, @@ -173,7 +173,7 @@ impl Default for PRUDPHeader{ } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub enum PacketOption{ SupportedFunctions(u32), ConnectionSignature([u8; 16]), @@ -236,7 +236,7 @@ impl PacketOption{ } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct PRUDPPacket { pub header: PRUDPHeader, pub packet_signature: [u8; 16], @@ -290,9 +290,12 @@ impl PRUDPPacket { let packet_signature: [u8; 16] = reader.read_struct(IS_BIG_ENDIAN)?; + //let packet_signature: [u8; 16] = [0; 16]; assert_eq!(reader.stream_position().ok(), Some(14 + 16)); + + let mut packet_specific_buffer = vec![0u8; header.packet_specific_size as usize]; reader.read_exact(&mut packet_specific_buffer)?; @@ -474,6 +477,8 @@ impl PRUDPPacket { #[cfg(test)] mod test { + use crate::prudp::packet::flags::{NEED_ACK, RELIABLE}; + use crate::prudp::packet::types::DATA; use super::{OptionId, PacketOption, PRUDPHeader, TypesFlags, VirtualPort}; #[test] fn size_test() { @@ -523,4 +528,13 @@ mod test { let header_data: [u8; 8] = bytes.try_into().unwrap(); } + + #[test] + fn test_types_flags(){ + let types = TypesFlags::default().types(DATA).flags(NEED_ACK | RELIABLE); + + assert_ne!((types.0 >> 4) & NEED_ACK, 0); + assert_ne!((types.0 >> 4) & RELIABLE, 0); + assert_ne!((types.0 & 0xFF) as u8 & DATA, 0); + } } \ No newline at end of file diff --git a/src/prudp/socket.rs b/src/prudp/socket.rs index c7b93ee..9068ba2 100644 --- a/src/prudp/socket.rs +++ b/src/prudp/socket.rs @@ -195,7 +195,7 @@ impl SocketData { } if (packet.header.types_and_flags.get_flags() & MULTI_ACK) != 0 { - println!("got ack"); + println!("got multi ack"); return; } @@ -458,6 +458,8 @@ impl SocketData { impl ConnectionData{ pub async fn finish_and_send_packet_to(&mut self, socket: &SocketData, mut packet: PRUDPPacket){ + + if (packet.header.types_and_flags.get_flags() & RELIABLE) != 0{ let Some(active_connection) = self.active_connection_data.as_mut() else { error!("tried to send a secure packet to an inactive connection"); @@ -491,7 +493,6 @@ impl ConnectionData{ packet.calculate_and_assign_signature(socket.access_key, potential_session_key, Some(self.server_signature)); - let mut vec = Vec::new(); packet.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); diff --git a/src/rmc/message.rs b/src/rmc/message.rs index 68598d2..f45b585 100644 --- a/src/rmc/message.rs +++ b/src/rmc/message.rs @@ -5,7 +5,7 @@ use log::error; use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; use crate::rmc::response::{ErrorCode, RMCResponseResult}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct RMCMessage{ pub protocol_id: u16, pub call_id: u32, @@ -60,13 +60,15 @@ impl RMCMessage{ output.write_all(bytes_of(&size)).expect("unable to write size"); - let proto_id = self.protocol_id as u8; + let proto_id = self.protocol_id as u8 | 0x80; output.write_all(bytes_of(&proto_id)).expect("unable to write size"); output.write_all(bytes_of(&self.call_id)).expect("unable to write size"); output.write_all(bytes_of(&self.method_id)).expect("unable to write size"); + output.write_all(&self.rest_of_data).expect("unable to write data"); + output } diff --git a/src/rmc/response.rs b/src/rmc/response.rs index 7d16ba4..ca02318 100644 --- a/src/rmc/response.rs +++ b/src/rmc/response.rs @@ -93,7 +93,7 @@ pub async fn send_response(original_packet: &PRUDPPacket, socket: &SocketData, c packet.header.types_and_flags.set_types(DATA); packet.header.types_and_flags.set_flag((original_packet.header.types_and_flags.get_flags() & RELIABLE) | NEED_ACK); - packet.header.session_id = active_connection.server_session_id; + //packet.header.session_id = active_connection.server_session_id; packet.header.substream_id = 0; packet.options.push(FragmentId(0)); From 3ea7c7e6718f509729de5b1d6a99d3179d4a997b Mon Sep 17 00:00:00 2001 From: DJMrTV Date: Tue, 18 Feb 2025 22:55:33 +0100 Subject: [PATCH 03/14] feat: refactor prudp code and start working on refactoring rmc --- Cargo.lock | 848 +++++++++++- Cargo.toml | 5 +- src/main.rs | 121 +- .../auth/method_login.rs | 1 - .../auth/method_login_ex.rs | 1 - .../auth/method_request_ticket.rs | 1 - .../auth/mod.rs | 0 .../auth/ticket_generation.rs | 0 .../matchmake/method_unregister_gathering.rs | 1 - .../matchmake/mod.rs | 0 .../matchmake_common/mod.rs | 3 +- ...thod_auto_matchmake_with_param_postpone.rs | 1 - ...hod_create_matchmake_session_with_param.rs | 38 +- .../method_get_playing_session.rs | 1 - .../matchmake_extension/mod.rs | 0 src/{protocols => nex-implementation}/mod.rs | 3 +- .../method_report_nat_properties.rs | 4 +- .../nat_traversal/mod.rs | 0 .../notification/mod.rs | 21 +- .../secure/method_register.rs | 1 - .../secure/method_send_report.rs | 8 +- .../secure/mod.rs | 0 .../server.rs | 10 +- src/prudp/mod.rs | 4 +- src/prudp/packet.rs | 6 +- src/prudp/router.rs | 29 +- src/prudp/secure.rs | 10 +- src/prudp/sockaddr.rs | 14 +- src/prudp/socket.rs | 1169 +++++++++++++---- src/rmc/mod.rs | 1 + src/rmc/protocols/mod.rs | 17 + src/rmc/response.rs | 33 +- src/rmc/structures/mod.rs | 1 + src/rmc/structures/qbuffer.rs | 32 +- src/rmc/structures/ranking.rs | 69 + src/rmc/structures/string.rs | 3 +- src/web/mod.rs | 37 + 37 files changed, 2033 insertions(+), 460 deletions(-) rename src/{protocols => nex-implementation}/auth/method_login.rs (93%) rename src/{protocols => nex-implementation}/auth/method_login_ex.rs (98%) rename src/{protocols => nex-implementation}/auth/method_request_ticket.rs (97%) rename src/{protocols => nex-implementation}/auth/mod.rs (100%) rename src/{protocols => nex-implementation}/auth/ticket_generation.rs (100%) rename src/{protocols => nex-implementation}/matchmake/method_unregister_gathering.rs (95%) rename src/{protocols => nex-implementation}/matchmake/mod.rs (100%) rename src/{protocols => nex-implementation}/matchmake_common/mod.rs (97%) rename src/{protocols => nex-implementation}/matchmake_extension/method_auto_matchmake_with_param_postpone.rs (98%) rename src/{protocols => nex-implementation}/matchmake_extension/method_create_matchmake_session_with_param.rs (70%) rename src/{protocols => nex-implementation}/matchmake_extension/method_get_playing_session.rs (95%) rename src/{protocols => nex-implementation}/matchmake_extension/mod.rs (100%) rename src/{protocols => nex-implementation}/mod.rs (98%) rename src/{protocols => nex-implementation}/nat_traversal/method_report_nat_properties.rs (89%) rename src/{protocols => nex-implementation}/nat_traversal/mod.rs (100%) rename src/{protocols => nex-implementation}/notification/mod.rs (86%) rename src/{protocols => nex-implementation}/secure/method_register.rs (97%) rename src/{protocols => nex-implementation}/secure/method_send_report.rs (82%) rename src/{protocols => nex-implementation}/secure/mod.rs (100%) rename src/{protocols => nex-implementation}/server.rs (86%) create mode 100644 src/rmc/protocols/mod.rs create mode 100644 src/rmc/structures/ranking.rs create mode 100644 src/web/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 0fbdcee..25373ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,6 +80,21 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "atomic" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" + +[[package]] +name = "atomic" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" +dependencies = [ + "bytemuck", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -127,8 +142,8 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http", - "http-body", + "http 1.2.0", + "http-body 1.0.1", "http-body-util", "itoa", "matchit", @@ -153,8 +168,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 1.2.0", + "http-body 1.0.1", "http-body-util", "mime", "pin-project-lite", @@ -176,7 +191,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -185,6 +200,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "binascii" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" + [[package]] name = "bindgen" version = "0.69.5" @@ -298,7 +319,7 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -331,6 +352,17 @@ dependencies = [ "cc", ] +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -356,6 +388,39 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "devise" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1d90b0c4c777a2cad215e3c7be59ac7c15adf45cf76317009b7d096d46f651d" +dependencies = [ + "devise_codegen", + "devise_core", +] + +[[package]] +name = "devise_codegen" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71b28680d8be17a570a2334922518be6adc3f58ecc880cbb404eaeb8624fd867" +dependencies = [ + "devise_core", + "quote", +] + +[[package]] +name = "devise_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b035a542cf7abf01f2e3c4d5a7acbaebfefe120ae4efc7bde3df98186e4b8af7" +dependencies = [ + "bitflags", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.96", +] + [[package]] name = "digest" version = "0.10.7" @@ -385,6 +450,15 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -407,6 +481,20 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "figment" +version = "0.10.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" +dependencies = [ + "atomic 0.6.0", + "pear", + "serde", + "toml", + "uncased", + "version_check", +] + [[package]] name = "fixedbitset" version = "0.4.2" @@ -425,6 +513,20 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.31" @@ -432,6 +534,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -440,6 +543,12 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + [[package]] name = "futures-sink" version = "0.3.31" @@ -458,10 +567,28 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ + "futures-channel", "futures-core", + "futures-io", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", + "slab", +] + +[[package]] +name = "generator" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +dependencies = [ + "cc", + "libc", + "log", + "rustversion", + "windows", ] [[package]] @@ -497,7 +624,7 @@ dependencies = [ "rustix", "wasi 0.13.3+wasi-0.2.2", "wasm-bindgen", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -512,6 +639,25 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.7.1", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.7" @@ -523,7 +669,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http", + "http 1.2.0", "indexmap 2.7.1", "slab", "tokio", @@ -549,6 +695,18 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "hex" version = "0.4.3" @@ -573,6 +731,17 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.2.0" @@ -584,6 +753,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.1" @@ -591,7 +771,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.2.0", ] [[package]] @@ -602,8 +782,8 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http", - "http-body", + "http 1.2.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -619,6 +799,30 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.6.0" @@ -628,9 +832,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2", - "http", - "http-body", + "h2 0.4.7", + "http 1.2.0", + "http-body 1.0.1", "httparse", "httpdate", "itoa", @@ -646,7 +850,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ - "hyper", + "hyper 1.6.0", "hyper-util", "pin-project-lite", "tokio", @@ -662,9 +866,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", - "hyper", + "http 1.2.0", + "http-body 1.0.1", + "hyper 1.6.0", "pin-project-lite", "socket2", "tokio", @@ -713,8 +917,15 @@ checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown 0.15.2", + "serde", ] +[[package]] +name = "inlinable_string" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" + [[package]] name = "inout" version = "0.1.3" @@ -724,6 +935,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "is-terminal" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "itertools" version = "0.12.1" @@ -783,7 +1005,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -792,12 +1014,37 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +[[package]] +name = "loom" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] + [[package]] name = "macros" version = "0.0.0" @@ -807,6 +1054,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matchit" version = "0.7.3" @@ -861,6 +1117,25 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "multer" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http 1.2.0", + "httparse", + "memchr", + "mime", + "spin", + "tokio", + "tokio-util", + "version_check", +] + [[package]] name = "multimap" version = "0.10.0" @@ -877,6 +1152,16 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -892,6 +1177,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + [[package]] name = "num_threads" version = "0.1.7" @@ -916,12 +1211,64 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pear" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" +dependencies = [ + "inlinable_string", + "pear_codegen", + "yansi", +] + +[[package]] +name = "pear_codegen" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.96", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1004,6 +1351,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", + "version_check", + "yansi", +] + [[package]] name = "prost" version = "0.13.4" @@ -1135,6 +1495,35 @@ dependencies = [ "cipher", ] +[[package]] +name = "redox_syscall" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +dependencies = [ + "bitflags", +] + +[[package]] +name = "ref-cast" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + [[package]] name = "regex" version = "1.11.1" @@ -1143,8 +1532,17 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -1155,9 +1553,15 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.5" @@ -1179,6 +1583,88 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rocket" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a516907296a31df7dc04310e7043b61d71954d703b603cc6867a026d7e72d73f" +dependencies = [ + "async-stream", + "async-trait", + "atomic 0.5.3", + "binascii", + "bytes", + "either", + "figment", + "futures", + "indexmap 2.7.1", + "log", + "memchr", + "multer", + "num_cpus", + "parking_lot", + "pin-project-lite", + "rand 0.8.5", + "ref-cast", + "rocket_codegen", + "rocket_http", + "serde", + "serde_json", + "state", + "tempfile", + "time", + "tokio", + "tokio-stream", + "tokio-util", + "ubyte", + "version_check", + "yansi", +] + +[[package]] +name = "rocket_codegen" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "575d32d7ec1a9770108c879fc7c47815a80073f96ca07ff9525a94fcede1dd46" +dependencies = [ + "devise", + "glob", + "indexmap 2.7.1", + "proc-macro2", + "quote", + "rocket_http", + "syn 2.0.96", + "unicode-xid", + "version_check", +] + +[[package]] +name = "rocket_http" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e274915a20ee3065f611c044bd63c40757396b6dbc057d6046aec27f14f882b9" +dependencies = [ + "cookie", + "either", + "futures", + "http 0.2.12", + "hyper 0.14.32", + "indexmap 2.7.1", + "log", + "memchr", + "pear", + "percent-encoding", + "pin-project-lite", + "ref-cast", + "serde", + "smallvec", + "stable-pattern", + "state", + "time", + "tokio", + "uncased", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -1243,6 +1729,24 @@ version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +[[package]] +name = "ryu" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.217" @@ -1263,12 +1767,51 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "serde_json" +version = "1.0.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "simplelog" version = "0.12.2" @@ -1316,6 +1859,7 @@ name = "splatoon-server-rust" version = "0.1.0" dependencies = [ "anyhow", + "async-trait", "bytemuck", "chrono", "dotenv", @@ -1328,7 +1872,9 @@ dependencies = [ "prost", "rand 0.9.0-beta.3", "rc4", + "rocket", "rustls", + "serde", "simplelog", "thiserror", "tokio", @@ -1338,6 +1884,24 @@ dependencies = [ "v_byte_macros", ] +[[package]] +name = "stable-pattern" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4564168c00635f88eaed410d5efa8131afa8d8699a612c80c455a0ba05c21045" +dependencies = [ + "memchr", +] + +[[package]] +name = "state" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" +dependencies = [ + "loom", +] + [[package]] name = "subtle" version = "2.6.1" @@ -1415,6 +1979,16 @@ dependencies = [ "syn 2.0.96", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "time" version = "0.3.37" @@ -1459,6 +2033,7 @@ dependencies = [ "libc", "mio", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.52.0", @@ -1499,6 +2074,40 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +dependencies = [ + "indexmap 2.7.1", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tonic" version = "0.12.3" @@ -1510,11 +2119,11 @@ dependencies = [ "axum", "base64", "bytes", - "h2", - "http", - "http-body", + "h2 0.4.7", + "http 1.2.0", + "http-body 1.0.1", "http-body-util", - "hyper", + "hyper 1.6.0", "hyper-timeout", "hyper-util", "percent-encoding", @@ -1618,6 +2227,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -1632,12 +2271,37 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "ubyte" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f720def6ce1ee2fc44d40ac9ed6d3a59c361c80a75a7aa8e75bb9baed31cf2ea" +dependencies = [ + "serde", +] + +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "serde", + "version_check", +] + [[package]] name = "unicode-ident" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "untrusted" version = "0.9.0" @@ -1654,6 +2318,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "version_check" version = "0.9.5" @@ -1754,6 +2424,22 @@ dependencies = [ "rustix", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.9" @@ -1763,13 +2449,28 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-core" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1778,7 +2479,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1787,7 +2488,22 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -1796,28 +2512,46 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -1830,30 +2564,63 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" +dependencies = [ + "memchr", +] + [[package]] name = "wit-bindgen-rt" version = "0.33.0" @@ -1863,6 +2630,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +dependencies = [ + "is-terminal", +] + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/Cargo.toml b/Cargo.toml index 321ded6..4c86ce9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,9 @@ prost = "0.13.4" hex = "0.4.3" macros = { path = "macros" } +rocket = { version = "0.5.1", features = ["json", "serde_json"] } +serde = { version = "1.0.217", features = ["derive"] } +async-trait = "0.1.86" [build-dependencies] tonic-build = "0.12.3" @@ -32,4 +35,4 @@ tonic-build = "0.12.3" [features] default = ["secure", "auth"] secure = [] -auth = [] \ No newline at end of file +auth = [] diff --git a/src/main.rs b/src/main.rs index 2b445c7..bb44bc5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,25 +14,21 @@ use simplelog::{ColorChoice, CombinedLogger, Config, LevelFilter, TerminalMode, use tokio::sync::RwLock; use tokio::task::JoinHandle; use crate::nex::account::Account; -use crate::protocols::{auth, block_if_maintenance}; -use crate::protocols::auth::AuthProtocolConfig; -use crate::protocols::matchmake_common::MatchmakeData; -use crate::protocols::server::RMCProtocolServer; -use crate::prudp::socket::{ActiveSecureConnectionData, EncryptionPair, Socket}; +use crate::prudp::socket::{EncryptionPair, Unsecure}; use crate::prudp::packet::{VirtualPort}; use crate::prudp::router::Router; -use crate::prudp::secure::{generate_secure_encryption_pairs, read_secure_connection_data}; -use crate::rmc::message::RMCMessage; -use crate::rmc::structures::RmcSerialize; +use crate::prudp::sockaddr::PRUDPSockAddr; +use crate::prudp::station_url::Type::PRUDP; mod endianness; mod prudp; pub mod rmc; -mod protocols; +//mod protocols; mod nex; mod grpc; mod kerberos; +mod web; static KERBEROS_SERVER_PASSWORD: Lazy = Lazy::new(||{ env::var("AUTH_SERVER_PASSWORD") @@ -97,6 +93,7 @@ async fn main() { start_servers().await; } +/* struct AuthServer{ router: Arc, @@ -236,74 +233,66 @@ async fn start_secure_server() -> SecureServer{ router, socket, } -} +}*/ async fn start_servers(){ + + + + let a = tokio::spawn(async{ + let (router_auth, _) = + Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *AUTH_SERVER_PORT)).await.expect("unable to start router"); + + let mut socket_auth = router_auth.add_socket(VirtualPort::new(1,10), Unsecure("CD&ML")).await + .expect("unable to add socket"); + + let mut conn = socket_auth.accept().await.unwrap(); + info!("got conn"); + + if let Some(data) = conn.recv().await{ + let str = String::from_utf8(data).unwrap(); + + println!("{}", str) + } + }); + + let b = tokio::spawn(async{ + let auth_ip = SocketAddrV4::new(*OWN_IP_PRIVATE, *AUTH_SERVER_PORT); + let auth_port = VirtualPort::new(1,10); + + let auth_sockaddr = PRUDPSockAddr::new(auth_ip,auth_port); + + + let (router_secure, _) = + Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *SECURE_SERVER_PORT)).await.expect("unable to start router"); + + let mut socket_secure = router_secure.add_socket(VirtualPort::new(1,10), Unsecure("CD&ML")).await + .expect("unable to add socket"); + + let conn = socket_secure.connect(auth_sockaddr).await.unwrap(); + + let conn = conn.duplicate_sender(); + + conn.send("Yippie".as_bytes().to_owned()).await; + + info!("got conn"); + }); + + a.await; + b.await; + +/* #[cfg(feature = "auth")] let auth_server = start_auth_server().await; #[cfg(feature = "secure")] let secure_server = start_secure_server().await; + let web_server = web::start_web().await; #[cfg(feature = "auth")] auth_server.join_handle.await.expect("auth server crashed"); #[cfg(feature = "secure")] secure_server.join_handle.await.expect("auth server crashed"); -} - - -#[cfg(test)] -mod test{ - use std::io::Cursor; - use std::num::ParseIntError; - use std::str::from_utf8; - use hmac::digest::consts::U5; - use rc4::{KeyInit, Rc4, StreamCipher}; - use crate::prudp::packet::PRUDPPacket; - use crate::rmc; - - fn from_hex_stream(val: &str) -> Result, ParseIntError> { - let res: Result, _> = val.as_bytes() - .chunks_exact(2) - .map(|c| from_utf8(c).expect("unable to convert back to string")) - .map(|s| u8::from_str_radix(s, 16)) - .collect(); - - res - } - - #[tokio::test] - async fn simulate_packets(){ - let val = from_hex_stream("ead001037d00afa1e200a5000200d9e4a4050368c18c6de4e2fb1cc40f0c020100768744db99f92c5005a061fd2a1df280cd64d5c1a565952c6befa607cbaf34661312b16db0fa6fccfb81e28b5a3a9bed02b49152bbc99cc112b7e29b9e45ec3d4b89df0fe71390883d9a927c264d07ada0de9cd28499e3ccdf3fd079e4a9848d4d783778c42da2af06106a7326634dc5bec5c3438ef18e30109839ffcc").expect("uuuuh"); - - let mut packet = PRUDPPacket::new(&mut Cursor::new(&val)).expect("invalid packet"); - - let mut rc4: Rc4 = - Rc4::new_from_slice("CD&ML".as_bytes().into()).expect("invalid key"); - - rc4.apply_keystream(&mut packet.payload); - - println!("packet: {:?}", packet); - - let rmc_packet = rmc::message::RMCMessage::new(&mut Cursor::new(&packet.payload)).expect("unable to read message"); - - let mut a = Cursor::new(&rmc_packet.rest_of_data); - - //let pid = rmc::structures::string::read(&mut a).expect("unable to read pid"); - } - - #[tokio::test] - async fn simulate_packets_response(){ - let val = from_hex_stream("ead001032501a1af6200a500010013ffcdbc3a2ebc44efc6e38ea32a72b40201002e8644db19fe2a5005a2637d2a16f3b1fe5633037c1ed61c5aefad8afebdf2ff8600e9350fba1298b570c70f6dd647eac2d3faf0ab74ef761e2ee43dc10e249e5f91aed6813dcc04b3c707d9442b6e353b9b0b654e98f860fe5379c41d3c2a1874b7dd37ebf499e03bd2fd3e9a9203c0959feb760c38f504dcd0c9e99b17fd410657da4efa3e01c8a68ab3042d6d489788d5580778d32249cdf1fba8bf68cf4019d116ea7c580622ea1e3635139d91b44635d5e95b6c35b33898fdc0117fa6fc7162840d07a49f1e7089aa0ea65409a8ddeb2334449ba73a0ff7de462cf4a706a696de0f0521b84ae5a3f8587f3585d202d3cc0fb0451519c1b830b5e3cdd6de52e9add7325cbbf08a7c2f8b875934942b226703a22b4bc8931932dab055049051e4144b02").expect("uuuuh"); - - let mut packet = PRUDPPacket::new(&mut Cursor::new(&val)).expect("invalid packet"); - - let mut rc4: Rc4 = - Rc4::new_from_slice("CD&ML".as_bytes().into()).expect("invalid key"); - - rc4.apply_keystream(&mut packet.payload); - - println!("packet: {:?}", packet); - } + web_server.await.expect("webserver crashed");*/ } diff --git a/src/protocols/auth/method_login.rs b/src/nex-implementation/auth/method_login.rs similarity index 93% rename from src/protocols/auth/method_login.rs rename to src/nex-implementation/auth/method_login.rs index d2c1642..aface70 100644 --- a/src/protocols/auth/method_login.rs +++ b/src/nex-implementation/auth/method_login.rs @@ -3,7 +3,6 @@ use std::sync::Arc; use log::error; use tokio::sync::Mutex; use crate::protocols::auth::AuthProtocolConfig; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponseResult}; use crate::rmc::structures::RmcSerialize; diff --git a/src/protocols/auth/method_login_ex.rs b/src/nex-implementation/auth/method_login_ex.rs similarity index 98% rename from src/protocols/auth/method_login_ex.rs rename to src/nex-implementation/auth/method_login_ex.rs index 63ff73f..413ab82 100644 --- a/src/protocols/auth/method_login_ex.rs +++ b/src/nex-implementation/auth/method_login_ex.rs @@ -7,7 +7,6 @@ use crate::grpc::account; use crate::kerberos::KerberosDateTime; use crate::protocols::auth::AuthProtocolConfig; use crate::protocols::auth::ticket_generation::generate_ticket; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponseResult}; diff --git a/src/protocols/auth/method_request_ticket.rs b/src/nex-implementation/auth/method_request_ticket.rs similarity index 97% rename from src/protocols/auth/method_request_ticket.rs rename to src/nex-implementation/auth/method_request_ticket.rs index 6f2d767..aa89448 100644 --- a/src/protocols/auth/method_request_ticket.rs +++ b/src/nex-implementation/auth/method_request_ticket.rs @@ -4,7 +4,6 @@ use tokio::sync::Mutex; use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; use crate::protocols::auth::{AuthProtocolConfig, get_login_data_by_pid}; use crate::protocols::auth::ticket_generation::generate_ticket; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponseResult}; use crate::rmc::response::ErrorCode::Core_Unknown; diff --git a/src/protocols/auth/mod.rs b/src/nex-implementation/auth/mod.rs similarity index 100% rename from src/protocols/auth/mod.rs rename to src/nex-implementation/auth/mod.rs diff --git a/src/protocols/auth/ticket_generation.rs b/src/nex-implementation/auth/ticket_generation.rs similarity index 100% rename from src/protocols/auth/ticket_generation.rs rename to src/nex-implementation/auth/ticket_generation.rs diff --git a/src/protocols/matchmake/method_unregister_gathering.rs b/src/nex-implementation/matchmake/method_unregister_gathering.rs similarity index 95% rename from src/protocols/matchmake/method_unregister_gathering.rs rename to src/nex-implementation/matchmake/method_unregister_gathering.rs index d055afb..22ec79f 100644 --- a/src/protocols/matchmake/method_unregister_gathering.rs +++ b/src/nex-implementation/matchmake/method_unregister_gathering.rs @@ -2,7 +2,6 @@ use std::io::Cursor; use std::sync::Arc; use tokio::sync::{Mutex, RwLock}; use crate::protocols::matchmake_common::MatchmakeData; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponseResult}; use crate::rmc::structures::qresult::QResult; diff --git a/src/protocols/matchmake/mod.rs b/src/nex-implementation/matchmake/mod.rs similarity index 100% rename from src/protocols/matchmake/mod.rs rename to src/nex-implementation/matchmake/mod.rs diff --git a/src/protocols/matchmake_common/mod.rs b/src/nex-implementation/matchmake_common/mod.rs similarity index 97% rename from src/protocols/matchmake_common/mod.rs rename to src/nex-implementation/matchmake_common/mod.rs index f016457..c4265d3 100644 --- a/src/protocols/matchmake_common/mod.rs +++ b/src/nex-implementation/matchmake_common/mod.rs @@ -5,7 +5,6 @@ use rand::random; use tokio::sync::{Mutex, RwLock}; use crate::kerberos::KerberosDateTime; use crate::protocols::notification::Notification; -use crate::prudp::socket::{ActiveConnectionData, ConnectionData, SocketData}; use crate::rmc::structures::matchmake::{Gathering, MatchmakeParam, MatchmakeSession}; use crate::rmc::structures::variant::Variant; @@ -44,7 +43,7 @@ impl ExtendedMatchmakeSession{ let mm_session = MatchmakeSession{ gathering: Gathering{ - self_gid: random(), + self_gid: 1, owner_pid: active_secure_connection_data.pid, host_pid: active_secure_connection_data.pid, ..session.gathering.clone() diff --git a/src/protocols/matchmake_extension/method_auto_matchmake_with_param_postpone.rs b/src/nex-implementation/matchmake_extension/method_auto_matchmake_with_param_postpone.rs similarity index 98% rename from src/protocols/matchmake_extension/method_auto_matchmake_with_param_postpone.rs rename to src/nex-implementation/matchmake_extension/method_auto_matchmake_with_param_postpone.rs index c822dc0..4c5d70a 100644 --- a/src/protocols/matchmake_extension/method_auto_matchmake_with_param_postpone.rs +++ b/src/nex-implementation/matchmake_extension/method_auto_matchmake_with_param_postpone.rs @@ -7,7 +7,6 @@ use rand::random; use tokio::sync::{Mutex, RwLock}; use tokio::time::sleep; use crate::protocols::matchmake_common::{ExtendedMatchmakeSession, MatchmakeData}; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponseResult}; use crate::rmc::structures::matchmake::{AutoMatchmakeParam, MatchmakeSession}; diff --git a/src/protocols/matchmake_extension/method_create_matchmake_session_with_param.rs b/src/nex-implementation/matchmake_extension/method_create_matchmake_session_with_param.rs similarity index 70% rename from src/protocols/matchmake_extension/method_create_matchmake_session_with_param.rs rename to src/nex-implementation/matchmake_extension/method_create_matchmake_session_with_param.rs index 6fa7214..438a9ed 100644 --- a/src/protocols/matchmake_extension/method_create_matchmake_session_with_param.rs +++ b/src/nex-implementation/matchmake_extension/method_create_matchmake_session_with_param.rs @@ -1,10 +1,11 @@ use std::io::Cursor; use std::sync::Arc; +use std::time::Duration; use log::info; use tokio::sync::{Mutex, RwLock}; +use tokio::time::sleep; use crate::protocols::matchmake_common::{add_matchmake_session, ExtendedMatchmakeSession, MatchmakeData}; use crate::protocols::matchmake_extension::method_auto_matchmake_with_param_postpone::auto_matchmake_with_param_postpone; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponseResult}; use crate::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam}; @@ -29,13 +30,17 @@ pub async fn create_matchmake_session_with_param( session.add_player(&socket, conn.clone(), create_matchmake_session.join_message).await; - println!("{:?}", session); + let mut response = Vec::new(); session.session.serialize(&mut response).expect("unable to serialize session"); + println!("{}", hex::encode(&response)); + + + rmcmessage.success_with_data(response) } @@ -51,7 +56,32 @@ pub async fn create_matchmake_session_with_param_raw_params( return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument); }; - - create_matchmake_session_with_param(rmcmessage, connection_data, socket, data, matchmake_param).await +} + +#[cfg(test)] +mod test{ + use std::io::Cursor; + use crate::prudp::packet::PRUDPPacket; + use crate::rmc::message::RMCMessage; + use crate::rmc::structures::matchmake::MatchmakeSession; + use crate::rmc::structures::RmcSerialize; + + #[test] + fn test(){ + let data = hex::decode("ead001030000a1af12001800050002010000000000000000000000000000000000").unwrap(); + + let packet = PRUDPPacket::new(&mut Cursor::new(data)).unwrap(); + + println!("{:?}", packet); + } + + #[test] + fn test_2(){ + let data = hex::decode("250000008e0100000001000000001700000051b39957b90b00000100000051b3995701000001000000").unwrap(); + + let msg = RMCMessage::new(&mut Cursor::new(data)).unwrap(); + + println!("{:?}", msg) + } } \ No newline at end of file diff --git a/src/protocols/matchmake_extension/method_get_playing_session.rs b/src/nex-implementation/matchmake_extension/method_get_playing_session.rs similarity index 95% rename from src/protocols/matchmake_extension/method_get_playing_session.rs rename to src/nex-implementation/matchmake_extension/method_get_playing_session.rs index bc4eb3c..d88a6f5 100644 --- a/src/protocols/matchmake_extension/method_get_playing_session.rs +++ b/src/nex-implementation/matchmake_extension/method_get_playing_session.rs @@ -3,7 +3,6 @@ use std::sync::Arc; use log::info; use tokio::sync::{Mutex, RwLock}; use crate::protocols::matchmake_common::MatchmakeData; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponseResult}; use crate::rmc::structures::RmcSerialize; diff --git a/src/protocols/matchmake_extension/mod.rs b/src/nex-implementation/matchmake_extension/mod.rs similarity index 100% rename from src/protocols/matchmake_extension/mod.rs rename to src/nex-implementation/matchmake_extension/mod.rs diff --git a/src/protocols/mod.rs b/src/nex-implementation/mod.rs similarity index 98% rename from src/protocols/mod.rs rename to src/nex-implementation/mod.rs index 51c37e1..16bf0bf 100644 --- a/src/protocols/mod.rs +++ b/src/nex-implementation/mod.rs @@ -6,7 +6,6 @@ use log::warn; use once_cell::sync::Lazy; use tokio::sync::Mutex; use crate::grpc; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponse}; @@ -17,7 +16,7 @@ pub mod secure; pub mod matchmake_extension; pub mod matchmake_common; pub mod matchmake; -mod notification; +pub mod notification; pub mod nat_traversal; static IS_MAINTENANCE: Lazy = Lazy::new(|| { diff --git a/src/protocols/nat_traversal/method_report_nat_properties.rs b/src/nex-implementation/nat_traversal/method_report_nat_properties.rs similarity index 89% rename from src/protocols/nat_traversal/method_report_nat_properties.rs rename to src/nex-implementation/nat_traversal/method_report_nat_properties.rs index 32ba30d..7d01740 100644 --- a/src/protocols/nat_traversal/method_report_nat_properties.rs +++ b/src/nex-implementation/nat_traversal/method_report_nat_properties.rs @@ -1,8 +1,9 @@ use std::io::Cursor; use std::sync::Arc; +use std::time::Duration; use tokio::sync::{Mutex, RwLock}; +use tokio::time::sleep; use crate::protocols::matchmake_common::MatchmakeData; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponseResult}; use crate::rmc::structures::matchmake::CreateMatchmakeSessionParam; @@ -12,6 +13,7 @@ pub async fn report_nat_properties( socket: &Arc, connection_data: &Arc>, ) -> RMCResponseResult{ + sleep(Duration::from_millis(50)).await; rmcmessage.success_with_data(Vec::new()) } diff --git a/src/protocols/nat_traversal/mod.rs b/src/nex-implementation/nat_traversal/mod.rs similarity index 100% rename from src/protocols/nat_traversal/mod.rs rename to src/nex-implementation/nat_traversal/mod.rs diff --git a/src/protocols/notification/mod.rs b/src/nex-implementation/notification/mod.rs similarity index 86% rename from src/protocols/notification/mod.rs rename to src/nex-implementation/notification/mod.rs index 87340a7..50cc992 100644 --- a/src/protocols/notification/mod.rs +++ b/src/nex-implementation/notification/mod.rs @@ -3,7 +3,6 @@ use rand::random; use crate::prudp::packet::{PRUDPHeader, PRUDPPacket, PacketOption, TypesFlags}; use crate::prudp::packet::flags::{NEED_ACK, RELIABLE}; use crate::prudp::packet::types::DATA; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::structures::RmcSerialize; @@ -30,10 +29,11 @@ impl ConnectionData{ let message = RMCMessage{ protocol_id: 14, method_id: 1, - call_id: random(), + call_id: 1, rest_of_data: data }; + println!("notif: {}", hex::encode(message.to_data())); let mut prudp_packet = PRUDPPacket{ @@ -99,23 +99,6 @@ mod test{ println!("{:?}", notif); } - #[test] - fn test3(){ - - let data = hex::decode("ead001032900a1af620084000300020100250000008e0100000001000000001700000051b39957b90b00000100000051b399570100000100000000000000000000000000000000000000").unwrap(); - let packet = PRUDPPacket::new(&mut Cursor::new(data)).expect("invalid packet"); - - println!("{:?}", packet); - - let rmc = RMCMessage::new(&mut Cursor::new(packet.payload)).expect("invalid rmc message"); - - println!("{:?}", rmc); - - let notif = Notification::deserialize(&mut Cursor::new(rmc.rest_of_data)).expect("invalid notification"); - - println!("{:?}", notif); - } - #[test] fn test_rmc_serialization(){ let notif = Notification{ diff --git a/src/protocols/secure/method_register.rs b/src/nex-implementation/secure/method_register.rs similarity index 97% rename from src/protocols/secure/method_register.rs rename to src/nex-implementation/secure/method_register.rs index fcfb438..0d1cd61 100644 --- a/src/protocols/secure/method_register.rs +++ b/src/nex-implementation/secure/method_register.rs @@ -2,7 +2,6 @@ use std::io::{Cursor, Write}; use std::sync::Arc; use bytemuck::bytes_of; use tokio::sync::Mutex; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::prudp::station_url::{nat_types, StationUrl}; use crate::prudp::station_url::Type::PRUDPS; use crate::prudp::station_url::UrlOptions::{Address, NatFiltering, NatMapping, NatType, Port, PrincipalID, RVConnectionID}; diff --git a/src/protocols/secure/method_send_report.rs b/src/nex-implementation/secure/method_send_report.rs similarity index 82% rename from src/protocols/secure/method_send_report.rs rename to src/nex-implementation/secure/method_send_report.rs index 81322f9..8242371 100644 --- a/src/protocols/secure/method_send_report.rs +++ b/src/nex-implementation/secure/method_send_report.rs @@ -3,11 +3,11 @@ use std::sync::Arc; use log::error; use tokio::sync::Mutex; use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::response::{RMCResponseResult}; use crate::rmc::response::ErrorCode::Core_InvalidArgument; -use crate::rmc::structures::qbuffer; +use crate::rmc::structures::{qbuffer, RmcSerialize}; +use crate::rmc::structures::qbuffer::QBuffer; pub async fn send_report(rmcmessage: &RMCMessage, report_id: u32, data: Vec) -> RMCResponseResult{ let result = tokio::fs::write(format!("./reports/{}", report_id), data).await; @@ -17,7 +17,7 @@ pub async fn send_report(rmcmessage: &RMCMessage, report_id: u32, data: Vec) Err(e) => error!("{}", e) } - return rmcmessage.success_with_data(Vec::new()); + rmcmessage.success_with_data(Vec::new()) } pub async fn send_report_raw_params(rmcmessage: &RMCMessage, _: &Arc, _conn_data: &Arc>, _: ()) -> RMCResponseResult{ @@ -27,7 +27,7 @@ pub async fn send_report_raw_params(rmcmessage: &RMCMessage, _: &Arc return rmcmessage.error_result_with_code(Core_InvalidArgument); }; - let Ok(data) = qbuffer::read(&mut reader) else { + let Ok(QBuffer(data)) = QBuffer::deserialize(&mut reader) else { return rmcmessage.error_result_with_code(Core_InvalidArgument); }; diff --git a/src/protocols/secure/mod.rs b/src/nex-implementation/secure/mod.rs similarity index 100% rename from src/protocols/secure/mod.rs rename to src/nex-implementation/secure/mod.rs diff --git a/src/protocols/server.rs b/src/nex-implementation/server.rs similarity index 86% rename from src/protocols/server.rs rename to src/nex-implementation/server.rs index 68fd7c2..dbf93de 100644 --- a/src/protocols/server.rs +++ b/src/nex-implementation/server.rs @@ -5,10 +5,11 @@ use std::sync::Arc; use log::error; use tokio::sync::Mutex; use crate::prudp::packet::PRUDPPacket; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::response::{RMCResponse, RMCResponseResult, send_response}; use crate::rmc::response::ErrorCode::Core_NotImplemented; +use crate::web::DirectionalData::Incoming; +use crate::web::WEB_DATA; type ContainedProtocolList = Box<[Box Fn(&'a RMCMessage, &'a Arc, &'a Arc>) -> Pin> + Send + 'a>> + Send + Sync>]>; @@ -20,6 +21,13 @@ impl RMCProtocolServer{ } pub async fn process_message(&self, packet: PRUDPPacket, socket: Arc, connection: Arc>){ + let locked = connection.lock().await; + let addr = locked.sock_addr.regular_socket_addr; + drop(locked); + let mut web = WEB_DATA.lock().await; + web.data.push((addr, Incoming(hex::encode(&packet.payload)))); + drop(web); + let Ok(rmc) = RMCMessage::new(&mut Cursor::new(&packet.payload)) else { error!("error reading rmc message"); return; diff --git a/src/prudp/mod.rs b/src/prudp/mod.rs index b86e522..41f721c 100644 --- a/src/prudp/mod.rs +++ b/src/prudp/mod.rs @@ -2,6 +2,6 @@ pub mod packet; pub mod router; pub mod socket; mod auth_module; -mod sockaddr; -pub mod secure; +pub mod sockaddr; +//pub mod secure; pub mod station_url; \ No newline at end of file diff --git a/src/prudp/packet.rs b/src/prudp/packet.rs index 470f6b2..75dc211 100644 --- a/src/prudp/packet.rs +++ b/src/prudp/packet.rs @@ -96,10 +96,11 @@ impl Debug for TypesFlags { } #[repr(transparent)] -#[derive(PartialEq, Eq, Copy, Clone, Pod, Zeroable, SwapEndian, Hash)] +#[derive(PartialEq, Eq, Ord, PartialOrd, Copy, Clone, Pod, Zeroable, SwapEndian, Hash)] pub struct VirtualPort(pub(crate) u8); impl VirtualPort { + #[inline] pub const fn get_stream_type(self) -> u8 { (self.0 & 0xF0) >> 4 @@ -236,7 +237,7 @@ impl PacketOption{ } } -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Default, Clone, Eq, PartialEq)] pub struct PRUDPPacket { pub header: PRUDPHeader, pub packet_signature: [u8; 16], @@ -375,6 +376,7 @@ impl PRUDPPacket { types_and_flags: flags, sequence_id: self.header.sequence_id, substream_id: self.header.substream_id, + session_id: self.header.session_id, ..base.header }, options, diff --git a/src/prudp/router.rs b/src/prudp/router.rs index f70d5ec..5fd4198 100644 --- a/src/prudp/router.rs +++ b/src/prudp/router.rs @@ -11,7 +11,7 @@ use once_cell::sync::Lazy; use log::{error, info, trace}; use thiserror::Error; use tokio::sync::RwLock; -use crate::prudp::socket::SocketData; +use crate::prudp::socket::{new_socket_pair, AnyInternalSocket, CryptoHandler, ExternalSocket}; use crate::prudp::packet::{PRUDPPacket, VirtualPort}; use crate::prudp::router::Error::VirtualPortTaken; @@ -22,10 +22,9 @@ static SERVER_DATAGRAMS: Lazy = Lazy::new(||{ }); pub struct Router { - endpoints: RwLock<[Option>; 16]>, + endpoints: RwLock<[Option>; 16]>, running: AtomicBool, socket: Arc, - //pub auth_module: Arc _no_outside_construction: PhantomData<()> } #[derive(Debug, Error)] @@ -36,9 +35,6 @@ pub enum Error{ impl Router { - fn process_prudp_packet(&self, _packet: &PRUDPPacket){ - - } async fn process_prudp_packets<'a>(self: Arc, _socket: Arc, addr: SocketAddrV4, udp_message: Vec){ let mut stream = Cursor::new(&udp_message); @@ -69,7 +65,9 @@ impl Router { trace!("sending packet to endpoint"); - endpoint.process_packet(connection, &packet).await; + tokio::spawn(async move { + endpoint.recieve_packet(connection, packet).await + }); } } @@ -88,6 +86,7 @@ impl Router { continue; }; + let current_msg = &msg_buffer[0..len]; tokio::spawn(self.clone().process_prudp_packets(socket.clone(), addr, current_msg.to_vec())); @@ -144,18 +143,22 @@ impl Router { } // returns Some(()) i - pub(crate) async fn add_socket(&self, socket: Arc) -> Result<(), Error>{ + pub(crate) async fn add_socket(&self, virtual_port: VirtualPort, encryption: E) + -> Result{ let mut endpoints = self.endpoints.write().await; - let idx = socket.get_virual_port().get_port_number() as usize; + let idx = virtual_port.get_port_number() as usize; - if endpoints[idx].is_none() { - endpoints[idx] = Some(socket); - } else { + // dont create the socket if we dont need to + if !endpoints[idx].is_none(){ return Err(VirtualPortTaken(idx as u8)); } - Ok(()) + let (internal, external) = new_socket_pair(virtual_port, encryption, self.socket.clone()); + + endpoints[idx] = Some(internal); + + Ok(external) } pub fn get_own_address(&self) -> SocketAddrV4{ diff --git a/src/prudp/secure.rs b/src/prudp/secure.rs index 916971d..2689963 100644 --- a/src/prudp/secure.rs +++ b/src/prudp/secure.rs @@ -73,12 +73,12 @@ pub fn read_secure_connection_data(data: &[u8], act: &Account) -> Option<([u8; 3 type Rc4U32 = StreamCipherCoreWrapper>; -pub fn generate_secure_encryption_pairs(mut session_key: [u8; 32], count: u8) -> Vec{ +pub fn generate_secure_encryption_pairs(mut session_key: [u8; 32], count: u8) -> Vec>>{ let mut vec = Vec::with_capacity(count as usize); vec.push(EncryptionPair{ - send: Box::new(Rc4U32::new_from_slice(&session_key).expect("unable to create rc4")), - recv: Box::new(Rc4U32::new_from_slice(&session_key).expect("unable to create rc4")) + send: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"), + recv: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4") }); for _ in 1..=count{ @@ -91,8 +91,8 @@ pub fn generate_secure_encryption_pairs(mut session_key: [u8; 32], count: u8) -> } vec.push(EncryptionPair{ - send: Box::new(Rc4U32::new_from_slice(&session_key).expect("unable to create rc4")), - recv: Box::new(Rc4U32::new_from_slice(&session_key).expect("unable to create rc4")) + send: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"), + recv: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4") }); } diff --git a/src/prudp/sockaddr.rs b/src/prudp/sockaddr.rs index b48b8c1..75254bf 100644 --- a/src/prudp/sockaddr.rs +++ b/src/prudp/sockaddr.rs @@ -5,14 +5,24 @@ use crate::prudp::packet::VirtualPort; type Md5Hmac = Hmac; -#[derive(Eq, PartialEq, Hash, Debug, Copy, Clone)] +#[derive(Eq, PartialEq, Hash, Debug, Copy, Clone, Ord, PartialOrd)] pub struct PRUDPSockAddr{ pub regular_socket_addr: SocketAddrV4, pub virtual_port: VirtualPort } + + impl PRUDPSockAddr{ - pub fn calculate_connection_signature(&self) -> [u8; 16] { + + pub fn new(regular_socket_addr: SocketAddrV4, virtual_port: VirtualPort) -> Self{ + Self{ + regular_socket_addr, + virtual_port + } + } + + pub(super) fn calculate_connection_signature(&self) -> [u8; 16] { let mut hmac = Md5Hmac::new_from_slice(&[0; 16]).expect("fuck"); let mut data = self.regular_socket_addr.ip().octets().to_vec(); diff --git a/src/prudp/socket.rs b/src/prudp/socket.rs index 9068ba2..9c4db03 100644 --- a/src/prudp/socket.rs +++ b/src/prudp/socket.rs @@ -1,156 +1,57 @@ -use log::info; -use std::collections::{HashMap, VecDeque}; -use std::fmt::{Debug, Formatter}; -use std::future::Future; -use std::ops::Deref; -use std::pin::Pin; -use tokio::net::UdpSocket; -use std::sync::{Arc}; -use tokio::sync::{Mutex, RwLock}; -use log::{error, trace, warn}; -use rand::random; -use rc4::StreamCipher; -use crate::prudp::packet::{PacketOption, PRUDPPacket, VirtualPort}; use crate::prudp::packet::flags::{ACK, HAS_SIZE, MULTI_ACK, NEED_ACK, RELIABLE}; -use crate::prudp::packet::PacketOption::{ConnectionSignature, MaximumSubstreamId, SupportedFunctions}; use crate::prudp::packet::types::{CONNECT, DATA, DISCONNECT, PING, SYN}; +use crate::prudp::packet::PacketOption::{ConnectionSignature, FragmentId, MaximumSubstreamId, SupportedFunctions}; +use crate::prudp::packet::{PRUDPHeader, PRUDPPacket, PacketOption, TypesFlags, VirtualPort}; use crate::prudp::router::{Error, Router}; use crate::prudp::sockaddr::PRUDPSockAddr; - - +use crate::web::DirectionalData::Outgoing; +use crate::web::WEB_DATA; +use async_trait::async_trait; +use hmac::digest::consts::U5; +use log::info; +use log::{error, trace, warn}; +use once_cell::sync::Lazy; +use rand::random; +use rc4::{Key, KeyInit, Rc4, StreamCipher}; +use rocket::http::hyper::body::HttpBody; +use std::collections::{BTreeMap, HashMap, VecDeque}; +use std::fmt::{Debug, Formatter}; +use std::future::Future; +use std::marker::PhantomData; +use std::mem; +use std::net::SocketAddrV4; +use std::ops::Deref; +use std::pin::Pin; +use std::sync::{Arc, Weak}; +use tokio::net::UdpSocket; +use tokio::sync::mpsc::{channel, Receiver, Sender}; +use tokio::sync::{Mutex, RwLock}; +use tokio_stream::Stream; // due to the way this is designed crashing the router thread causes deadlock, sorry ;-; // (maybe i will fix that some day) /// PRUDP Socket for accepting connections to then send and recieve data from those clients -pub struct Socket { - socket_data: Arc, - router: Arc, + + +pub struct EncryptionPair { + pub send: T, + pub recv: T, } - -type OnConnectHandlerFn = Box Pin, Vec, Option)>> + Send>> + Send + Sync>; -type OnDataHandlerFn = Box, Arc>) -> Pin + Send>> + Send + Sync>; - -#[derive(Debug)] -pub struct ActiveSecureConnectionData { - pub(crate) pid: u32, - pub(crate) session_key: [u8; 32], -} - -pub struct SocketData { - virtual_port: VirtualPort, - pub socket: Arc, - pub access_key: &'static str, - connections: RwLock>, Arc>)>>, - on_connect_handler: OnConnectHandlerFn, - on_data_handler: OnDataHandlerFn, - -} - -pub struct EncryptionPair{ - pub send: Box, - pub recv: Box -} - -impl Debug for EncryptionPair{ - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "Stubbed") - } -} - -#[derive(Debug)] -pub struct ActiveConnectionData { - pub reliable_client_counter: u16, - pub reliable_server_counter: u16, - pub reliable_client_queue: VecDeque, - pub encryption_pairs: Vec, - pub server_session_id: u8, - pub connection_id: u32, - pub active_secure_connection_data: Option -} - - -#[derive(Debug)] -pub struct ConnectionData { - pub sock_addr: PRUDPSockAddr, - pub id: u64, - pub signature: [u8; 16], - pub server_signature: [u8; 16], - pub active_connection_data: Option, -} - - - - -impl Socket { - pub async fn new( - router: Arc, - port: VirtualPort, - access_key: &'static str, - on_connection_handler: OnConnectHandlerFn, - on_data_handler: OnDataHandlerFn, - ) -> Result { - trace!("creating socket on router at {} on virtual port {:?}", router.get_own_address(), port); - - let socket_data = Arc::new( - SocketData::new_unbound(&router, port, access_key, on_connection_handler, on_data_handler) - ); - - router.add_socket(socket_data.clone()).await?; - - Ok(Self { - socket_data, - router, - }) - } -} - -impl Drop for Socket { - fn drop(&mut self) { - { - let router = self.router.clone(); - - let virtual_port = self.virtual_port; - trace!("socket dropped socket will be removed from router soon"); - // it's not that important to remove it immediately so we can delay the deletion a bit if needed - tokio::spawn(async move { - router.remove_socket(virtual_port).await; - trace!("socket removed from router successfully"); - }); +impl EncryptionPair { + fn init_both T>(func: F) -> Self { + Self { + recv: func(), + send: func(), } } } - -impl Deref for Socket { - type Target = SocketData; - fn deref(&self) -> &Self::Target { - &self.socket_data - } -} - - -impl SocketData { - fn new_unbound(router: &Router, - port: VirtualPort, - access_key: &'static str, - on_connect_handler: OnConnectHandlerFn, - on_data_handler: OnDataHandlerFn, - ) -> Self { - SocketData { - socket: router.get_udp_socket(), - virtual_port: port, - connections: Default::default(), - access_key, - on_connect_handler, - on_data_handler, - } - } - - pub fn get_virual_port(&self) -> VirtualPort { - self.virtual_port - } - - pub async fn process_packet(self: &Arc, client_address: PRUDPSockAddr, packet: &PRUDPPacket) { +/* + pub async fn process_packet( + self: &Arc, + client_address: PRUDPSockAddr, + packet: &PRUDPPacket, + ) { let conn = self.connections.read().await; if !conn.contains_key(&client_address) { @@ -159,14 +60,20 @@ impl SocketData { let mut conn = self.connections.write().await; //only insert if we STILL dont have the connection preventing double insertion if !conn.contains_key(&client_address) { - conn.insert(client_address, (Arc::new(Mutex::new(ConnectionData { - sock_addr: client_address, - id: random(), - signature: [0; 16], - server_signature: [0; 16], + conn.insert( + client_address, + ( + Arc::new(Mutex::new(ConnectionData { + sock_addr: client_address, + id: random(), + signature: [0; 16], + server_signature: [0; 16], - active_connection_data: None, - })), Arc::new(Mutex::new(())))); + active_connection_data: None, + })), + Arc::new(Mutex::new(())), + ), + ); } drop(conn); } else { @@ -199,7 +106,6 @@ impl SocketData { return; } - match packet.header.types_and_flags.get_types() { SYN => { println!("got syn"); @@ -212,16 +118,18 @@ impl SocketData { connection.signature = client_address.calculate_connection_signature(); - response_packet.options.push(ConnectionSignature(connection.signature)); + response_packet + .options + .push(ConnectionSignature(connection.signature)); for options in &packet.options { match options { - SupportedFunctions(functions) => { - response_packet.options.push(SupportedFunctions(*functions & 0x04)) - } - MaximumSubstreamId(max_substream) => { - response_packet.options.push(MaximumSubstreamId(*max_substream)) - } + SupportedFunctions(functions) => response_packet + .options + .push(SupportedFunctions(*functions & 0x04)), + MaximumSubstreamId(max_substream) => response_packet + .options + .push(MaximumSubstreamId(*max_substream)), _ => { /* ??? */ } } } @@ -232,26 +140,32 @@ impl SocketData { let mut vec = Vec::new(); - response_packet.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); + response_packet + .write_to(&mut vec) + .expect("somehow failed to convert backet to bytes"); - self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back"); + self.socket + .send_to(&vec, client_address.regular_socket_addr) + .await + .expect("failed to send data back"); } CONNECT => { println!("got connect"); - let Some(MaximumSubstreamId(max_substream)) = packet.options.iter().find(|v| matches!(v, MaximumSubstreamId(_))) else { + let Some(MaximumSubstreamId(max_substream)) = packet + .options + .iter() + .find(|v| matches!(v, MaximumSubstreamId(_))) + else { return; }; - let Some(( - response_data, - encryption_pairs, - active_secure_connection_data - )) = (self.on_connect_handler)(packet.clone(), *max_substream).await else { + let Some((response_data, encryption_pairs, active_secure_connection_data)) = + (self.on_connect_handler)(packet.clone(), *max_substream).await + else { error!("invalid connection request"); return; }; - connection.active_connection_data = Some(ActiveConnectionData { encryption_pairs, reliable_client_queue: VecDeque::new(), @@ -259,7 +173,7 @@ impl SocketData { reliable_server_counter: 1, server_session_id: packet.header.session_id, active_secure_connection_data, - connection_id: random() + connection_id: random(), }); let mut response_packet = packet.base_response_packet(); @@ -275,17 +189,21 @@ impl SocketData { response_packet.header.session_id = packet.header.session_id; response_packet.header.sequence_id = 1; - response_packet.options.push(ConnectionSignature(Default::default())); + response_packet + .options + .push(ConnectionSignature(Default::default())); //let mut init_seq_id = 0; for option in &packet.options { match option { - MaximumSubstreamId(max_substream) => response_packet.options.push(MaximumSubstreamId(*max_substream)), - SupportedFunctions(funcs) => response_packet.options.push(SupportedFunctions(*funcs)), - ConnectionSignature(sig) => { - connection.server_signature = *sig + MaximumSubstreamId(max_substream) => response_packet + .options + .push(MaximumSubstreamId(*max_substream)), + SupportedFunctions(funcs) => { + response_packet.options.push(SupportedFunctions(*funcs)) } + ConnectionSignature(sig) => connection.server_signature = *sig, PacketOption::InitialSequenceId(_id) => { //init_seq_id = *id; } @@ -305,14 +223,21 @@ impl SocketData { response_packet.set_sizes(); - response_packet.calculate_and_assign_signature(self.access_key, None, Some(connection.server_signature)); + response_packet.calculate_and_assign_signature( + self.access_key, + None, + Some(connection.server_signature), + ); let mut vec = Vec::new(); - response_packet.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); - - self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back"); - + response_packet + .write_to(&mut vec) + .expect("somehow failed to convert backet to bytes"); + self.socket + .send_to(&vec, client_address.regular_socket_addr) + .await + .expect("failed to send data back"); } DATA => { if (packet.header.types_and_flags.get_flags() & RELIABLE) != 0 { @@ -321,12 +246,16 @@ impl SocketData { return; }; - match active_connection.reliable_client_queue.binary_search_by_key(&packet.header.sequence_id, |p| p.header.sequence_id) { + match active_connection + .reliable_client_queue + .binary_search_by_key(&packet.header.sequence_id, |p| p.header.sequence_id) + { Ok(_) => warn!("recieved packet twice"), - Err(position) => active_connection.reliable_client_queue.insert(position, packet.clone()), + Err(position) => active_connection + .reliable_client_queue + .insert(position, packet.clone()), } - if (packet.header.types_and_flags.get_flags() & NEED_ACK) != 0 { let mut ack = packet.base_acknowledgement_packet(); ack.header.session_id = active_connection.server_session_id; @@ -335,34 +264,51 @@ impl SocketData { let potential_session_key = connection .active_connection_data .as_ref() - .unwrap().active_secure_connection_data + .unwrap() + .active_secure_connection_data .as_ref() .map(|s| s.session_key); - ack.calculate_and_assign_signature(self.access_key, potential_session_key, Some(connection.server_signature)); + ack.calculate_and_assign_signature( + self.access_key, + potential_session_key, + Some(connection.server_signature), + ); let mut vec = Vec::new(); - ack.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); + ack.write_to(&mut vec) + .expect("somehow failed to convert backet to bytes"); - self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back"); + self.socket + .send_to(&vec, client_address.regular_socket_addr) + .await + .expect("failed to send data back"); } drop(connection); while let Some(mut packet) = { let mut locked = conn.0.lock().await; - let packet = locked.active_connection_data.as_mut().map(|a| - a.reliable_client_queue - .front() - .is_some_and(|v| v.header.sequence_id == a.reliable_client_counter) - .then(|| a.reliable_client_queue.pop_front())).flatten().flatten(); + let packet = locked + .active_connection_data + .as_mut() + .map(|a| { + a.reliable_client_queue + .front() + .is_some_and(|v| { + v.header.sequence_id == a.reliable_client_counter + }) + .then(|| a.reliable_client_queue.pop_front()) + }) + .flatten() + .flatten(); drop(locked); packet } { - if packet.options.iter().any(|v| match v{ + if packet.options.iter().any(|v| match v { PacketOption::FragmentId(f) => *f != 0, _ => false, - }){ + }) { error!("fragmented packets are unsupported right now") } @@ -371,9 +317,16 @@ impl SocketData { let active_connection = locked.active_connection_data.as_mut() .expect("we litterally just recieved a packet which requires the connection to be active, failing this should be impossible"); - active_connection.reliable_client_counter = active_connection.reliable_client_counter.overflowing_add(1).0; + active_connection.reliable_client_counter = active_connection + .reliable_client_counter + .overflowing_add(1) + .0; - let Some(stream) = active_connection.encryption_pairs.get_mut(packet.header.substream_id as usize).map(|e| &mut e.recv) else { + let Some(stream) = active_connection + .encryption_pairs + .get_mut(packet.header.substream_id as usize) + .map(|e| &mut e.recv) + else { return; }; @@ -411,17 +364,25 @@ impl SocketData { ack.set_sizes(); - let potential_session_key = active_connection. - active_secure_connection_data + let potential_session_key = active_connection + .active_secure_connection_data .as_ref() .map(|s| s.session_key); - ack.calculate_and_assign_signature(self.access_key, potential_session_key, Some(*server_signature)); + ack.calculate_and_assign_signature( + self.access_key, + potential_session_key, + Some(*server_signature), + ); let mut vec = Vec::new(); - ack.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); + ack.write_to(&mut vec) + .expect("somehow failed to convert backet to bytes"); - self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back"); + self.socket + .send_to(&vec, client_address.regular_socket_addr) + .await + .expect("failed to send data back"); } } DISCONNECT => { @@ -436,31 +397,57 @@ impl SocketData { ack.set_sizes(); - let potential_session_key = active_connection.active_secure_connection_data + let potential_session_key = active_connection + .active_secure_connection_data .as_ref() .map(|s| s.session_key); - ack.calculate_and_assign_signature(self.access_key, potential_session_key, Some(connection.server_signature)); + ack.calculate_and_assign_signature( + self.access_key, + potential_session_key, + Some(connection.server_signature), + ); let mut vec = Vec::new(); - ack.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); + ack.write_to(&mut vec) + .expect("somehow failed to convert backet to bytes"); - self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back"); - self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back"); - self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back"); + self.socket + .send_to(&vec, client_address.regular_socket_addr) + .await + .expect("failed to send data back"); + self.socket + .send_to(&vec, client_address.regular_socket_addr) + .await + .expect("failed to send data back"); + self.socket + .send_to(&vec, client_address.regular_socket_addr) + .await + .expect("failed to send data back"); } - _ => error!("unimplemented packet type: {}", packet.header.types_and_flags.get_types()) + _ => error!( + "unimplemented packet type: {}", + packet.header.types_and_flags.get_types() + ), } drop(mutual_exclusion_packet_handeling_mtx) - } -} + }*/ +/* +impl ConnectionData { + pub async fn finish_and_send_packet_to( + &mut self, + socket: &SocketData, + mut packet: PRUDPPacket, + ) { + let mut web = WEB_DATA.lock().await; + web.data.push(( + self.sock_addr.regular_socket_addr, + Outgoing(hex::encode(&packet.payload)), + )); + drop(web); -impl ConnectionData{ - pub async fn finish_and_send_packet_to(&mut self, socket: &SocketData, mut packet: PRUDPPacket){ - - - if (packet.header.types_and_flags.get_flags() & RELIABLE) != 0{ + if (packet.header.types_and_flags.get_flags() & RELIABLE) != 0 { let Some(active_connection) = self.active_connection_data.as_mut() else { error!("tried to send a secure packet to an inactive connection"); return; @@ -469,80 +456,748 @@ impl ConnectionData{ packet.header.sequence_id = active_connection.reliable_server_counter; active_connection.reliable_server_counter += 1; - let Some(encryption) = active_connection.encryption_pairs.get_mut(packet.header.substream_id as usize).map(|e| &mut e.send) else { + let Some(encryption) = active_connection + .encryption_pairs + .get_mut(packet.header.substream_id as usize) + .map(|e| &mut e.send) + else { return; }; encryption.apply_keystream(&mut packet.payload); } - packet.header.session_id = self.active_connection_data.as_ref().map(|v| v.server_session_id).unwrap_or_default(); - - + packet.header.session_id = self + .active_connection_data + .as_ref() + .map(|v| v.server_session_id) + .unwrap_or_default(); packet.header.source_port = socket.virtual_port; packet.header.destination_port = self.sock_addr.virtual_port; packet.set_sizes(); - let potential_session_key = self.active_connection_data + let potential_session_key = self + .active_connection_data .as_ref() - .unwrap().active_secure_connection_data + .unwrap() + .active_secure_connection_data .as_ref() .map(|s| s.session_key); - - packet.calculate_and_assign_signature(socket.access_key, potential_session_key, Some(self.server_signature)); + packet.calculate_and_assign_signature( + socket.access_key, + potential_session_key, + Some(self.server_signature), + ); let mut vec = Vec::new(); - packet.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); + packet + .write_to(&mut vec) + .expect("somehow failed to convert backet to bytes"); - if let Err(e) = socket.socket.send_to(&vec, self.sock_addr.regular_socket_addr).await{ + if let Err(e) = socket + .socket + .send_to(&vec, self.sock_addr.regular_socket_addr) + .await + { error!("unable to send packet to destination: {}", e); } } - - +}*/ + +pub struct NewEncryptionPair { + pub send: E, + pub recv: E, } -#[cfg(test)] -mod test { - use std::io::Cursor; - use std::net::{Ipv4Addr, SocketAddrV4}; - use std::sync::Arc; - use tokio::net::UdpSocket; - use tokio::sync::mpsc::channel; - use crate::prudp::packet::{PRUDPPacket, VirtualPort}; - use crate::prudp::sockaddr::PRUDPSockAddr; - use crate::prudp::socket::SocketData; +pub struct CommonConnection { + pub user_id: u32, + session_id: u8, + pub socket_addr: PRUDPSockAddr, + pub server_port: VirtualPort +} - /*#[tokio::test] - async fn test_connect() { - let packet_1 = [234, 208, 1, 27, 0, 0, 175, 161, 192, 0, 0, 0, 0, 0, 36, 21, 233, 179, 203, 154, 57, 222, 219, 9, 21, 2, 29, 172, 56, 92, 0, 4, 4, 1, 0, 0, 1, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 0]; - let packet_2 = [234, 208, 1, 31, 0, 0, 175, 161, 225, 0, 249, 0, 1, 0, 40, 168, 31, 138, 58, 193, 30, 134, 3, 232, 205, 245, 28, 155, 193, 198, 0, 4, 0, 0, 0, 0, 1, 16, 211, 240, 113, 188, 227, 114, 114, 30, 157, 179, 246, 55, 233, 240, 44, 197, 3, 2, 247, 244, 4, 1, 0]; +struct InternalConnection { + common: Arc, + reliable_server_counter: u16, + reliable_client_counter: u16, + // maybe add connection id(need to see if its even needed) + crypto_handler_instance: E, + data_sender: Sender>, + socket: Arc +} - let packet_1 = PRUDPPacket::new(&mut Cursor::new(packet_1)).unwrap(); - let packet_2 = PRUDPPacket::new(&mut Cursor::new(packet_2)).unwrap(); +impl Deref for InternalConnection{ + type Target = CommonConnection; + fn deref(&self) -> &Self::Target { + &self.common + } +} + +impl InternalConnection{ + fn next_server_count(&mut self) -> u16{ + let (val, _) = self.reliable_server_counter.overflowing_add(1); + self.reliable_server_counter = val; + val + } +} + +pub struct ExternalConnection { + sending: SendingConnection, + data_receiver: Receiver>, +} + +#[derive(Clone)] +pub struct SendingConnection{ + common: Arc, + inernal: Weak> +} + +pub struct CommonSocket { + pub virtual_port: VirtualPort, + _phantom_unconstructible: PhantomData<()>, +} + +pub(super) struct InternalSocket { + common: Arc, + socket: Arc, + crypto_handler: T, + // perf note: change the code to use RwLock here instead to avoid connections being able to block one another before the data is sent off. + internal_connections: Arc< + Mutex>>>>, + >, + connection_establishment_data_sender: Mutex>>, + connection_sender: Sender, +} + +pub struct ExternalSocket { + common: Arc, + connection_receiver: Receiver, + internal: Weak, +} + +impl ExternalSocket{ + pub async fn connect(&mut self, addr: PRUDPSockAddr) -> Option{ + let socket = self.internal.upgrade()?; + + socket.connect(addr).await; + + self.connection_receiver.recv().await + } + + pub async fn accept(&mut self) -> Option{ + self.connection_receiver.recv().await + } +} + +impl Deref for ExternalSocket { + type Target = CommonSocket; + fn deref(&self) -> &Self::Target { + &self.common + } +} + +impl Deref for InternalSocket { + type Target = CommonSocket; + fn deref(&self) -> &Self::Target { + &self.common + } +} + +#[async_trait] +pub(super) trait AnyInternalSocket: + Send + Sync + Deref + 'static +{ + async fn recieve_packet(&self, address: PRUDPSockAddr, packet: PRUDPPacket); + async fn connect(&self, address: PRUDPSockAddr) -> Option<()>; +} + +#[async_trait] +pub(super) trait AnyInternalConnection: + Send + Sync + Deref + 'static +{ + async fn send_data_packet(&mut self, data: Vec); +} + +#[async_trait] +impl AnyInternalConnection for InternalConnection{ + async fn send_data_packet(&mut self, data: Vec) { + let mut packet = PRUDPPacket{ + header: PRUDPHeader{ + sequence_id: self.next_server_count(), + substream_id: 0, + session_id: self.session_id, + types_and_flags: TypesFlags::default().types(DATA).flags(RELIABLE | NEED_ACK), + destination_port: self.common.socket_addr.virtual_port, + source_port: self.server_port, + ..Default::default() + }, + payload: data, + options: vec![FragmentId(0)], + ..Default::default() + }; + + self.crypto_handler_instance.encrypt_outgoing(0, &mut packet.payload[..]); + + packet.set_sizes(); + + self.crypto_handler_instance.sign_packet(&mut packet); + + packet.set_sizes(); + + let mut vec = Vec::new(); + + packet + .write_to(&mut vec) + .expect("somehow failed to convert backet to bytes"); + + self.socket + .send_to(&vec, self.socket_addr.regular_socket_addr) + .await + .expect("failed to send data back"); + } +} + +impl InternalSocket { + async fn send_packet_unbuffered(&self, dest: PRUDPSockAddr, mut packet: PRUDPPacket) { + packet.set_sizes(); + + let mut vec = Vec::new(); + + packet + .write_to(&mut vec) + .expect("somehow failed to convert backet to bytes"); + + self.socket + .send_to(&vec, dest.regular_socket_addr) + .await + .expect("failed to send data back"); + } + + async fn handle_syn(&self, address: PRUDPSockAddr, packet: PRUDPPacket) { + info!("got syn"); + + let mut response = packet.base_acknowledgement_packet(); + + let signature = address.calculate_connection_signature(); + + response.options.push(ConnectionSignature(signature)); + + // todo: refactor this to be more readable(low priority cause it doesnt change anything api wise) + for options in &packet.options { + match options { + SupportedFunctions(functions) => response + .options + .push(SupportedFunctions(*functions & 0x04)), + MaximumSubstreamId(max_substream) => response + .options + .push(MaximumSubstreamId(*max_substream)), + _ => { /* ??? */ } + } + } + + response.header.types_and_flags.set_flag(HAS_SIZE | ACK); + + self.crypto_handler.sign_pre_handshake(&mut response); + + self.send_packet_unbuffered(address, response) + .await; + } + + async fn connection_thread( + socket: Arc, + self_port: VirtualPort, + connection: Arc>>, + mut data_recv: Receiver> + ) { + //todo: handle stuff like resending packets if they arent acknowledged in here + while let Some(data) = data_recv.recv().await{ + let mut locked_conn = connection.lock().await; + let packet = PRUDPPacket{ + header: PRUDPHeader{ + sequence_id: locked_conn.next_server_count(), + substream_id: 0, + session_id: locked_conn.session_id, + types_and_flags: TypesFlags::default().types(DATA).flags(RELIABLE | NEED_ACK), + destination_port: locked_conn.common.socket_addr.virtual_port, + source_port: self_port, + ..Default::default() + }, + payload: data, + options: vec![FragmentId(0)], + ..Default::default() + }; + + //packet. - let (send, recv) = channel(100); - let sock = Arc::new(SocketData { - connections: Default::default(), - access_key: "6f599f81", - virtual_port: VirtualPort(0), - socket: Arc::new(UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 10000)).await.unwrap()), - connection_creation_sender: send, + + + + } + } + + async fn create_connection( + &self, + crypto_handler_instance: T::CryptoConnectionInstance, + socket_addr: PRUDPSockAddr, + session_id: u8, + ) { + let common = Arc::new(CommonConnection { + user_id: crypto_handler_instance.get_user_id(), + socket_addr, + session_id, + server_port: self.virtual_port }); - println!("sent: {:?}", packet_1); - sock.process_packet(PRUDPSockAddr { - virtual_port: VirtualPort(0), - regular_socket_addr: SocketAddrV4::new(Ipv4Addr::LOCALHOST, 2469), - }, &packet_1).await; - println!("sent: {:?}", packet_2); - sock.process_packet(PRUDPSockAddr { - virtual_port: VirtualPort(0), - regular_socket_addr: SocketAddrV4::new(Ipv4Addr::LOCALHOST, 2469), - }, &packet_2).await; - }*/ -} \ No newline at end of file + + let (data_sender_from_client, data_receiver_from_client) = channel(16); + + let internal = InternalConnection { + common: common.clone(), + crypto_handler_instance, + reliable_client_counter: 2, + reliable_server_counter: 1, + data_sender: data_sender_from_client, + socket: self.socket.clone() + }; + + let internal = Arc::new(Mutex::new(internal)); + + let dyn_internal: Arc> = internal.clone(); + + let external = ExternalConnection { + sending: SendingConnection{ + common, + inernal: Arc::downgrade(&dyn_internal) + }, + data_receiver: data_receiver_from_client, + + }; + + + + + + let mut connections = self.internal_connections.lock().await; + + connections.insert(socket_addr, internal.clone()); + + drop(connections); + + self.connection_sender + .send(external) + .await + .expect("connection to external socket lost"); + } + + async fn handle_connect(&self, address: PRUDPSockAddr, packet: PRUDPPacket) { + info!("got connect"); + let Some(MaximumSubstreamId(max_substream)) = packet + .options + .iter() + .find(|v| matches!(v, MaximumSubstreamId(_))) + else { + return; + }; + + let remote_signature = address.calculate_connection_signature(); + + let Some(ConnectionSignature(own_signature)) = packet + .options + .iter() + .find(|p| matches!(p, ConnectionSignature(_))) + else { + error!("didnt get connection signature from client"); + return; + }; + + let session_id = packet.header.session_id; + + let (return_data, crypto) = self.crypto_handler.instantiate( + remote_signature, + *own_signature, + &packet.payload, + *max_substream, + ); + + let mut response = packet.base_acknowledgement_packet(); + response.header.types_and_flags.set_flag(HAS_SIZE | ACK); + response.header.session_id = session_id; + response.payload = return_data; + + crypto.sign_connect(&mut response); + + self.create_connection(crypto, address, session_id).await; + + self.send_packet_unbuffered(address, response).await; + } + + async fn handle_data(&self, address: PRUDPSockAddr, mut packet: PRUDPPacket) { + info!("got data"); + + if packet.header.types_and_flags.get_flags() & (NEED_ACK | RELIABLE) != (NEED_ACK | RELIABLE){ + error!("invalid or unimplemented packet flags"); + } + + let connections = self.internal_connections.lock().await; + let Some(conn) = connections.get(&address) else{ + error!("tried to send data on inactive connection!"); + return + }; + let conn = conn.clone(); + drop(connections); + + let mut conn = conn.lock().await; + + conn.crypto_handler_instance.decrypt_incoming(packet.header.substream_id, &mut packet.payload[..]); + + let mut data = Vec::new(); + + mem::swap(&mut data, &mut packet.payload); + + conn.data_sender.send(data).await.expect("socket died"); + + if packet.header.types_and_flags.get_flags() & NEED_ACK == 0{ + return; + } + + let mut response = packet.base_acknowledgement_packet(); + response.header.types_and_flags.set_flag(HAS_SIZE | ACK); + response.header.session_id = conn.session_id; + + conn.crypto_handler_instance.sign_packet(&mut response); + + self.send_packet_unbuffered(address, response).await; + } + + async fn handle_ping(&self, address: PRUDPSockAddr, packet: PRUDPPacket){ + let connections = self.internal_connections.lock().await; + let Some(conn) = connections.get(&address) else{ + error!("tried to send data on inactive connection!"); + return + }; + let conn = conn.clone(); + drop(connections); + + let mut conn = conn.lock().await; + + let mut response = packet.base_acknowledgement_packet(); + response.header.types_and_flags.set_flag(HAS_SIZE | ACK); + response.header.session_id = conn.session_id; + + conn.crypto_handler_instance.sign_packet(&mut response); + + self.send_packet_unbuffered(address, response).await; + } + + async fn handle_disconnect(&self, address: PRUDPSockAddr, packet: PRUDPPacket){ + let connections = self.internal_connections.lock().await; + let Some(conn) = connections.get(&address) else{ + error!("tried to send data on inactive connection!"); + return + }; + let conn = conn.clone(); + drop(connections); + + let mut conn = conn.lock().await; + + let mut response = packet.base_acknowledgement_packet(); + response.header.types_and_flags.set_flag(HAS_SIZE | ACK); + response.header.session_id = conn.session_id; + + conn.crypto_handler_instance.sign_packet(&mut response); + + self.send_packet_unbuffered(address, response.clone()).await; + self.send_packet_unbuffered(address, response.clone()).await; + self.send_packet_unbuffered(address, response).await; + } +} + +#[async_trait] +impl AnyInternalSocket for InternalSocket { + async fn recieve_packet(&self, address: PRUDPSockAddr, packet: PRUDPPacket) { + // todo: handle acks + if (packet.header.types_and_flags.get_flags() & ACK) != 0 { + info!("got ack"); + if packet.header.types_and_flags.get_types() == SYN || + packet.header.types_and_flags.get_types() == CONNECT{ + let sender = self.connection_establishment_data_sender.lock().await; + info!("redirecting ack to active connection establishment code"); + + if let Some(conn) = sender.as_ref(){ + if let Err(e) = conn.send(packet).await { + error!("error whilest sending data to connection establishment: {}", e); + } + } else { + error!("got connection response without the active reciever being present"); + } + } + return; + } + + if (packet.header.types_and_flags.get_flags() & MULTI_ACK) != 0 { + info!("got multi ack"); + return; + } + + match packet.header.types_and_flags.get_types() { + SYN => self.handle_syn(address, packet).await, + CONNECT => self.handle_connect(address, packet).await, + DATA => self.handle_data(address, packet).await, + _ => { + error!( + "unimplemented packet type: {}", + packet.header.types_and_flags.get_types() + ) + } + } + } + + async fn connect(&self, address: PRUDPSockAddr) -> Option<()> { + let (send, mut recv) = channel(10); + + let mut sender = self.connection_establishment_data_sender.lock().await; + *sender = Some(send); + drop(sender); + + let remote_signature = address.calculate_connection_signature(); + + let packet = PRUDPPacket{ + header: PRUDPHeader{ + source_port: self.virtual_port, + destination_port: address.virtual_port, + types_and_flags: TypesFlags::default().types(SYN).flags(NEED_ACK), + ..Default::default() + }, + options: vec![ + SupportedFunctions(0x104), + MaximumSubstreamId(1), + ConnectionSignature(remote_signature) + ], + ..Default::default() + }; + + + + self.send_packet_unbuffered(address, packet).await; + + let Some(syn_ack_packet) = recv.recv().await else{ + error!("what"); + return None; + }; + + let Some(ConnectionSignature(own_signature)) = syn_ack_packet + .options + .iter() + .find(|p| matches!(p, ConnectionSignature(_))) + else { + error!("didnt get connection signature from remote partner"); + return None; + }; + + + + let packet = PRUDPPacket{ + header: PRUDPHeader{ + source_port: self.virtual_port, + destination_port: address.virtual_port, + types_and_flags: TypesFlags::default().types(CONNECT).flags(NEED_ACK), + ..Default::default() + }, + options: vec![ + SupportedFunctions(0x04), + MaximumSubstreamId(1), + ConnectionSignature(remote_signature) + ], + ..Default::default() + }; + + self.send_packet_unbuffered(address, packet).await; + + let Some(connect_ack_packet) = recv.recv().await else{ + error!("what"); + return None; + }; + + let (_, crypt) = self.crypto_handler.instantiate(remote_signature, *own_signature, &[], 1); + + //todo: make this work for secure servers as well + self.create_connection(crypt, address, 0).await; + + Some(()) + } +} + +pub(super) fn new_socket_pair( + virtual_port: VirtualPort, + encryption: T, + socket: Arc, +) -> (Arc>, ExternalSocket) { + let common = Arc::new(CommonSocket { + virtual_port, + _phantom_unconstructible: Default::default(), + }); + + let (connection_send, connection_recv) = channel(16); + + let internal = Arc::new(InternalSocket { + common: common.clone(), + connection_sender: connection_send, + crypto_handler: encryption, + internal_connections: Default::default(), + connection_establishment_data_sender: Default::default(), + socket, + }); + + let dyn_internal: Arc = internal.clone(); + + let external = ExternalSocket { + common, + connection_receiver: connection_recv, + internal: Arc::downgrade(&dyn_internal), + }; + + (internal, external) +} + +pub trait CryptoHandlerConnectionInstance: Send + Sync + 'static { + type Encryption: StreamCipher + Send; + + fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]); + fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]); + + fn get_user_id(&self) -> u32; + fn sign_connect(&self, packet: &mut PRUDPPacket); + fn sign_packet(&self, packet: &mut PRUDPPacket); + fn verify_packet(&self, packet: &PRUDPPacket) -> bool; +} + +pub trait CryptoHandler: Send + Sync + 'static { + type CryptoConnectionInstance: CryptoHandlerConnectionInstance; + + fn instantiate( + &self, + remote_signature: [u8; 16], + own_signature: [u8; 16], + _: &[u8], + substream_count: u8, + ) -> (Vec, Self::CryptoConnectionInstance); + + fn sign_pre_handshake(&self, packet: &mut PRUDPPacket); +} + +impl Deref for ExternalConnection{ + type Target = SendingConnection; + fn deref(&self) -> &Self::Target { + &self.sending + } +} + +impl Deref for SendingConnection{ + type Target = CommonConnection; + fn deref(&self) -> &Self::Target { + &self.common + } +} + +impl ExternalConnection{ + pub async fn recv(&mut self) -> Option>{ + self.data_receiver.recv().await + } + //todo: make this an actual result instead of an option + + pub fn duplicate_sender(&self) -> SendingConnection{ + self.sending.clone() + } +} + +impl SendingConnection{ + pub async fn send(&self, data: Vec) -> Option<()> { + let internal = self.inernal.upgrade()?; + + let mut internal = internal.lock().await; + + internal.send_data_packet(data).await; + Some(()) + } +} + +pub struct Unsecure(pub &'static str); + +pub struct UnsecureInstance { + key: &'static str, + streams: Vec>>, + self_signature: [u8; 16], + remote_signature: [u8; 16], +} + +// my hand was forced to use lazy so that we can guarantee this code +// only runs once and so that i can put it here as a "constant" (for performance and readability) +// since for some reason rust crypto doesn't have any const time key initialization +static DEFAULT_KEY: Lazy> = Lazy::new(|| Key::from(*b"CD&ML")); + +impl CryptoHandler for Unsecure { + type CryptoConnectionInstance = UnsecureInstance; + + fn instantiate( + &self, + remote_signature: [u8; 16], + self_signature: [u8; 16], + _: &[u8], + substream_count: u8, + ) -> (Vec, Self::CryptoConnectionInstance) { + ( + Vec::new(), + UnsecureInstance { + streams: (0..substream_count) + .map(|_| EncryptionPair::init_both(|| Rc4::new(&DEFAULT_KEY))) + .collect(), + key: self.0, + remote_signature, + self_signature, + }, + ) + } + + fn sign_pre_handshake(&self, packet: &mut PRUDPPacket) { + packet.set_sizes(); + packet.calculate_and_assign_signature(self.0, None, None); + } +} + +impl CryptoHandlerConnectionInstance for UnsecureInstance { + type Encryption = Rc4; + + fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]) { + if let Some(crypt_pair) = self.streams.get_mut(substream as usize){ + crypt_pair.recv.apply_keystream(data); + } + } + + fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]) { + if let Some(crypt_pair) = self.streams.get_mut(substream as usize){ + crypt_pair.send.apply_keystream(data); + } + } + + fn get_user_id(&self) -> u32 { + 0 + } + + fn sign_connect(&self, packet: &mut PRUDPPacket) { + packet.set_sizes(); + packet.calculate_and_assign_signature(self.key, None, Some(self.self_signature)); + } + + fn sign_packet(&self, packet: &mut PRUDPPacket) { + packet.set_sizes(); + packet.calculate_and_assign_signature(self.key, None, Some(self.self_signature)); + } + + fn verify_packet(&self, packet: &PRUDPPacket) -> bool { + true + } +} diff --git a/src/rmc/mod.rs b/src/rmc/mod.rs index bf1eb0c..23458f6 100644 --- a/src/rmc/mod.rs +++ b/src/rmc/mod.rs @@ -1,6 +1,7 @@ pub mod message; pub mod structures; pub mod response; +pub mod protocols; diff --git a/src/rmc/protocols/mod.rs b/src/rmc/protocols/mod.rs new file mode 100644 index 0000000..2762de2 --- /dev/null +++ b/src/rmc/protocols/mod.rs @@ -0,0 +1,17 @@ +use std::sync::Arc; +use async_trait::async_trait; +use tokio::sync::Mutex; +use crate::prudp::socket::ExternalConnection; +use crate::rmc::structures::connection_data::ConnectionData; + +pub trait RmcCallable{ + fn rmc_call(protocol_id: u8, method_id: u8, rest: Vec); +} + +struct LocalRmcObjectWrapper(T); +impl LocalRmcObjectWrapper{ + pub fn new(object: T, conn: ExternalConnection) -> Self{ + unimplemented!() + } +} + diff --git a/src/rmc/response.rs b/src/rmc/response.rs index ca02318..bbbfb13 100644 --- a/src/rmc/response.rs +++ b/src/rmc/response.rs @@ -6,8 +6,10 @@ use crate::prudp::packet::{PRUDPPacket}; use crate::prudp::packet::flags::{NEED_ACK, RELIABLE}; use crate::prudp::packet::PacketOption::FragmentId; use crate::prudp::packet::types::DATA; -use crate::prudp::socket::{ConnectionData, SocketData}; +use crate::prudp::socket::ExternalConnection; use crate::rmc::structures::qresult::ERROR_MASK; +use crate::web::DirectionalData::{Incoming, Outgoing}; +use crate::web::WEB_DATA; pub enum RMCResponseResult { Success{ @@ -76,33 +78,8 @@ pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Re Ok(data_out) } -pub async fn send_response(original_packet: &PRUDPPacket, socket: &SocketData, connection: &mut ConnectionData, rmcresponse: RMCResponse){ - - let ConnectionData{ - active_connection_data, - .. - } = connection; - - let Some(active_connection) = active_connection_data else { - return; - }; - - let mut packet = original_packet.base_response_packet(); - - - packet.header.types_and_flags.set_types(DATA); - packet.header.types_and_flags.set_flag((original_packet.header.types_and_flags.get_flags() & RELIABLE) | NEED_ACK); - - //packet.header.session_id = active_connection.server_session_id; - packet.header.substream_id = 0; - - packet.options.push(FragmentId(0)); - - packet.payload = rmcresponse.to_data(); - - //tokio::time::sleep(Duration::from_millis(500)).await; - - connection.finish_and_send_packet_to(socket, packet).await; +pub async fn send_response(original_packet: &PRUDPPacket, connection: &mut ExternalConnection, rmcresponse: RMCResponse){ + connection.send(rmcresponse.to_data()).await; } //taken from kinnays error list directly diff --git a/src/rmc/structures/mod.rs b/src/rmc/structures/mod.rs index fc4e9d9..667af6e 100644 --- a/src/rmc/structures/mod.rs +++ b/src/rmc/structures/mod.rs @@ -30,6 +30,7 @@ pub mod qbuffer; pub mod primitives; pub mod matchmake; pub mod variant; +mod ranking; pub trait RmcSerialize: Sized{ fn serialize(&self, writer: &mut dyn Write) -> Result<()>; diff --git a/src/rmc/structures/qbuffer.rs b/src/rmc/structures/qbuffer.rs index 09fc8e3..f87c49e 100644 --- a/src/rmc/structures/qbuffer.rs +++ b/src/rmc/structures/qbuffer.rs @@ -1,12 +1,30 @@ -use std::io::Read; +use std::io::{Read, Write}; +use bytemuck::bytes_of; use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; -use crate::rmc::structures::Result; -pub fn read(reader: &mut impl Read) -> Result>{ - let size: u16 = reader.read_struct(IS_BIG_ENDIAN)?; +use crate::rmc::structures::{Result, RmcSerialize}; +use crate::rmc::structures::qresult::QResult; - let mut vec = vec![0; size as usize]; - reader.read_exact(&mut vec)?; +#[derive(Debug)] +pub struct QBuffer(pub Vec); - Ok(vec) +impl RmcSerialize for QBuffer{ + fn serialize(&self, writer: &mut dyn Write) -> Result<()> { + let len_u16 = self.0.len() as u16; + + writer.write(bytes_of(&len_u16))?; + writer.write(&self.0)?; + + Ok(()) + } + + fn deserialize(mut reader: &mut dyn Read) -> Result { + let size: u16 = reader.read_struct(IS_BIG_ENDIAN)?; + + let mut vec = vec![0; size as usize]; + + reader.read_exact(&mut vec)?; + + Ok(Self(vec)) + } } \ No newline at end of file diff --git a/src/rmc/structures/ranking.rs b/src/rmc/structures/ranking.rs new file mode 100644 index 0000000..c912d3f --- /dev/null +++ b/src/rmc/structures/ranking.rs @@ -0,0 +1,69 @@ +use bytemuck::{Pod, Zeroable}; +use macros::RmcSerialize; +use crate::rmc::structures::qbuffer::QBuffer; + +#[derive(RmcSerialize, Debug)] +#[rmc_struct(0)] +struct UploadCompetitionData{ + winning_team/*?*/: u32, + splatfest_id/*?*/: u32, + unk_2/*?*/: u32, + unk_3: u32, + team_id_1: u8, + team_id_2: u8, + unk_5: u32, + player_data/*?*/: QBuffer, +} + +#[derive(Copy, Clone, Pod, Zeroable)] +#[repr(C)] +struct UserData{ + name: [u16; 0x10], +} + +#[cfg(test)] +mod test{ + use std::io::Cursor; + use bytemuck::from_bytes; + use tokio::io::AsyncReadExt; + use crate::rmc::structures::ranking::{UploadCompetitionData, UserData}; + use crate::rmc::structures::RmcSerialize; + + #[test] + fn test() { + let data: [u8; 0xBD] = [ + 0x00, 0xB8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x1F, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x49, 0x00, + 0x7A, 0x00, 0x7A, 0x00, 0x79, 0x00, 0x53, 0x00, 0x50, 0x00, 0x46, 0x00, 0x4E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0xF2, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1F, 0x5E, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x90, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0A, 0x00, 0x00, 0x14, 0x87, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, + ]; + + let mut cursor = Cursor::new(data); + + let data = UploadCompetitionData::deserialize(&mut cursor).expect("unable to deserialize data"); + + let user_data: &UserData = from_bytes(&data.player_data.0[..size_of::()]); + + let pos = user_data.name.iter() + .position(|v| *v == 0x0000) + .unwrap_or(0x10); + + let mut name = user_data.name[0..pos].to_vec(); + + name.iter_mut().for_each(|v| *v = v.swap_bytes()); + + let name = String::from_utf16(&name).expect("unable to get name"); + + println!("{:?}", name); + + assert!(u8::deserialize(&mut cursor).is_err()) + } +} \ No newline at end of file diff --git a/src/rmc/structures/string.rs b/src/rmc/structures/string.rs index 712da2c..690e6a1 100644 --- a/src/rmc/structures/string.rs +++ b/src/rmc/structures/string.rs @@ -35,4 +35,5 @@ impl RmcSerialize for &str{ Ok(()) } -} \ No newline at end of file +} + diff --git a/src/web/mod.rs b/src/web/mod.rs new file mode 100644 index 0000000..3461257 --- /dev/null +++ b/src/web/mod.rs @@ -0,0 +1,37 @@ +use std::net::SocketAddrV4; +use once_cell::sync::Lazy; +use rocket::{get, routes, Rocket}; +use rocket::serde::json::Json; +use tokio::task::JoinHandle; +use serde::Serialize; +use tokio::sync::Mutex; + +#[get("/")] +async fn server_data() -> Json { + Json(WEB_DATA.lock().await.clone()) +} + +pub async fn start_web() -> JoinHandle<()>{ + tokio::spawn(async{ + rocket::build() + .mount("/",routes![server_data]) + .launch().await + .expect("unable to start webserver"); + }) +} +#[derive(Serialize, Clone)] +pub enum DirectionalData{ + Incoming(String), + Outgoing(String) +} + +#[derive(Serialize, Default, Clone)] +pub struct WebData{ + pub data: Vec<(SocketAddrV4, DirectionalData)> +} + +pub static WEB_DATA: Lazy> = Lazy::new(|| Mutex::new( + WebData{ + data: Vec::new(), + } +)); \ No newline at end of file From 6f02339464ec072b282886cfdf33a9261fce905b Mon Sep 17 00:00:00 2001 From: DJMrTV Date: Wed, 19 Feb 2025 23:30:15 +0100 Subject: [PATCH 04/14] feat(rmc): start implementing new rmc protocol abstraction --- Cargo.lock | 51 +++++------ Cargo.toml | 1 + macros/Cargo.lock | 4 +- macros/Cargo.toml | 6 +- macros/src/lib.rs | 188 +++++++++++++++++++++++++++++++-------- src/rmc/protocols/mod.rs | 112 ++++++++++++++++++++--- 6 files changed, 284 insertions(+), 78 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25373ac..a8ac40e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,7 +66,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -77,7 +77,7 @@ checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -225,7 +225,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.96", + "syn 2.0.98", "which", ] @@ -267,7 +267,7 @@ checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -418,7 +418,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1051,7 +1051,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.98", ] [[package]] @@ -1266,7 +1266,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1302,7 +1302,7 @@ checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1339,7 +1339,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1359,7 +1359,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "version_check", "yansi", ] @@ -1390,7 +1390,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.96", + "syn 2.0.98", "tempfile", ] @@ -1404,7 +1404,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1521,7 +1521,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1633,7 +1633,7 @@ dependencies = [ "proc-macro2", "quote", "rocket_http", - "syn 2.0.96", + "syn 2.0.98", "unicode-xid", "version_check", ] @@ -1764,7 +1764,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1869,6 +1869,7 @@ dependencies = [ "macros", "md-5", "once_cell", + "paste", "prost", "rand 0.9.0-beta.3", "rc4", @@ -1921,9 +1922,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.96" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -1976,7 +1977,7 @@ checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2047,7 +2048,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2149,7 +2150,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2217,7 +2218,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2376,7 +2377,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "wasm-bindgen-shared", ] @@ -2398,7 +2399,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2666,7 +2667,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2677,7 +2678,7 @@ checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 4c86ce9..394af2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ macros = { path = "macros" } rocket = { version = "0.5.1", features = ["json", "serde_json"] } serde = { version = "1.0.217", features = ["derive"] } async-trait = "0.1.86" +paste = "1.0.15" [build-dependencies] tonic-build = "0.12.3" diff --git a/macros/Cargo.lock b/macros/Cargo.lock index b79f6d0..a82b7df 100644 --- a/macros/Cargo.lock +++ b/macros/Cargo.lock @@ -31,9 +31,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.109" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", diff --git a/macros/Cargo.toml b/macros/Cargo.toml index a8c2072..d0bb83b 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -10,7 +10,7 @@ edition = "2018" proc-macro = true [dependencies] -quote = "1" -proc-macro2 = "1.0" -syn = "1.0" +quote = "1.0.38" +proc-macro2 = "1.0.93" +syn = { version = "2.0.98", features = ["full"] } diff --git a/macros/src/lib.rs b/macros/src/lib.rs index c4a2c8b..bbd2923 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,11 +1,14 @@ extern crate proc_macro; -use proc_macro2::TokenTree; +use proc_macro2::{Ident, Literal, Span, TokenTree}; use proc_macro::TokenStream; - -use syn::{parse_macro_input, DeriveInput, Data}; -use quote::{quote, TokenStreamExt}; - +use std::iter::FromIterator; +use syn::{parse_macro_input, DeriveInput, Data, PathSegment, TraitItem, FieldsNamed, Fields, Visibility, Type, TypePath, Path, ImplItem, ImplItemConst, Expr, ExprLit, Lit}; +use quote::{quote, ToTokens, TokenStreamExt}; +use syn::parse::ParseStream; +use syn::punctuated::Punctuated; +use syn::spanned::Spanned; +use syn::Visibility::Public; /// Example of user-defined [derive mode macro][1] /// @@ -15,8 +18,8 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { let derive_input = parse_macro_input!(input as DeriveInput); let struct_attr = derive_input.attrs.iter() - .find(|a| a.path.segments.len() == 1 && - a.path.segments.first().is_some_and(|p| p.ident.to_string() == "rmc_struct")); + .find(|a| a.path().segments.len() == 1 && + a.path().segments.first().is_some_and(|p| p.ident.to_string() == "rmc_struct")); let Data::Struct(s) = derive_input.data else { panic!("rmc struct type MUST be a struct"); @@ -29,8 +32,8 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { for f in &s.fields{ if f.attrs.iter() - .any(|a| a.path.segments.len() == 1 && - a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends")){ + .any(|a| a.path().segments.len() == 1 && + a.path().segments.first().is_some_and(|p| p.ident.to_string() == "extends")){ continue; } let ident = f.ident.as_ref().unwrap(); @@ -67,8 +70,8 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { for f in &s.fields{ if f.attrs.iter() - .any(|a| a.path.segments.len() == 1 && - a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends")){ + .any(|a| a.path().segments.len() == 1 && + a.path().segments.first().is_some_and(|p| p.ident.to_string() == "extends")){ continue; } @@ -89,23 +92,12 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { // generate base with extends stuff let serialize_base_content = if let Some(attr) = struct_attr{ - let tokens = attr.tokens.clone(); - let token = tokens.into_iter().next().unwrap(); - - let version = match token { - TokenTree::Group(g) => { - match g.stream().into_iter().next().unwrap(){ - TokenTree::Literal(l) => l, - _ => panic!("expected literal") - } - }, - _ => panic!("expected group") - }; + let version: Literal = attr.parse_args().expect("has to be a literal"); let pre_inner = if let Some(f) = s.fields.iter().find(|f| { f.attrs.iter() - .any(|a| a.path.segments.len() == 1 && - a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends")) + .any(|a| a.path().segments.len() == 1 && + a.path().segments.first().is_some_and(|p| p.ident.to_string() == "extends")) }){ let ident= f.ident.as_ref().unwrap(); quote! { @@ -128,22 +120,12 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { }; let deserialize_base_content = if let Some(attr) = struct_attr{ - let tokens = attr.tokens.clone(); - let token = tokens.into_iter().next().unwrap(); + let version: Literal = attr.parse_args().expect("has to be a literal"); - let version = match token { - TokenTree::Group(g) => { - match g.stream().into_iter().next().unwrap(){ - TokenTree::Literal(l) => l, - _ => panic!("expected literal") - } - }, - _ => panic!("expected group") - }; let pre_inner = if let Some(f) = s.fields.iter().find(|f| { f.attrs.iter() - .any(|a| a.path.segments.len() == 1 && - a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends")) + .any(|a| a.path().segments.len() == 1 && + a.path().segments.first().is_some_and(|p| p.ident.to_string() == "extends")) }){ let ident= f.ident.as_ref().unwrap(); let ty= &f.ty; @@ -181,4 +163,134 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { }; tokens.into() +} + +#[proc_macro_attribute] +pub fn rmc_proto(attr: TokenStream, input: TokenStream) -> TokenStream{ + let mut proto_num = parse_macro_input!(attr as syn::LitInt); + + let mut input = parse_macro_input!(input as syn::ItemTrait); + + let info_struct_ident = format!("Raw{}Info",input.ident.to_string()); + let info_struct_ident = Ident::new(&info_struct_ident, input.ident.span()); + + let raw_details_struct = syn::ItemStruct{ + vis: Visibility::Public(Default::default()), + struct_token: Default::default(), + fields: Fields::Unit, + semi_token: Some(Default::default()), + ident: info_struct_ident.clone(), + attrs: vec![], + generics: Default::default() + }; + + let raw_details_impl_block = syn::ItemImpl{ + impl_token: Default::default(), + generics: Default::default(), + attrs: vec![], + brace_token: Default::default(), + defaultness: None, + trait_: None, + self_ty: Box::new(Type::Path(TypePath{ + qself: None, + path: Path{ + segments: { + let mut punc = Punctuated::new(); + punc.push(PathSegment::from(info_struct_ident)); + punc + }, + leading_colon: None, + } + })), + unsafety: None, + items: vec![ + ImplItem::Const( + ImplItemConst{ + defaultness: None, + semi_token: Default::default(), + attrs: vec![], + generics: Default::default(), + ident: Ident::new("PROTOCOL_ID", Span::call_site()), + vis: Public(Default::default()), + colon_token: Default::default(), + const_token: Default::default(), + eq_token: Default::default(), + expr: Expr::Lit(ExprLit{ + attrs: vec![], + lit: Lit::Int(proto_num), + }), + ty: Type::Path(TypePath{ + qself: None, + path: Path{ + segments: { + let mut punc = Punctuated::new(); + punc.push(PathSegment::from(Ident::new("u16", Span::call_site()))); + punc + }, + leading_colon: None, + } + }) + } + ) + ] + }; + + let funcs = input.items.iter().filter_map(|v| if let TraitItem::Fn(v) = v {Some(v)} else { None }); + + for func in funcs{ + if matches!(func.default, Some(_)){ + return syn::Error::new(func.default.span(), "rmc methods may not have bodies").to_compile_error().into(); + } + + let Some(attr) = func.attrs.iter() + .find(|a| a.path().segments.last().is_some_and(|s| s.ident.to_string() == "method_id")) else { + let span = func.sig.asyncness.span().join(func.semi_token.unwrap().span()).unwrap_or(func.sig.span()); + return syn::Error::new(span, "every function inside of an rmc protocol must have a method id").to_compile_error().into(); + }; + + todo!("generate raw impl") + } + + quote!{ + #input + #raw_details_struct + #raw_details_impl_block + }.into() + +} + + +#[proc_macro_attribute] +pub fn method_id(_attr: TokenStream, input: TokenStream) -> TokenStream{ + // this attribute doesnt do anything by itself, see `rmc_proto` + input +} + + +#[proc_macro_attribute] +pub fn rmc_struct(attr: TokenStream, input: TokenStream) -> TokenStream{ + let mut type_data = parse_macro_input!(input as DeriveInput); + let mut ident = parse_macro_input!(attr as syn::Path); + let last_token = ident.segments.last_mut().expect("empty path?"); + + last_token.ident = Ident::new(&("Local".to_owned() + &last_token.ident.to_string()), last_token.span()); + + + let struct_name = &type_data.ident; + + let out = quote!{ + #type_data + + impl #ident for #struct_name{ + + } + + impl crate::rmc::protocols::RmcCallable for #struct_name{ + async fn rmc_call(&self, protocol_id: u16, method_id: u32, rest: Vec){ + ::rmc_call(self, protocol_id, method_id, rest).await; + } + } + }; + + out.into() } \ No newline at end of file diff --git a/src/rmc/protocols/mod.rs b/src/rmc/protocols/mod.rs index 2762de2..5025cb4 100644 --- a/src/rmc/protocols/mod.rs +++ b/src/rmc/protocols/mod.rs @@ -1,17 +1,109 @@ -use std::sync::Arc; +use macros::method_id; +use std::collections::HashMap; +use std::ops::Add; +use std::sync::{Arc, Condvar}; +use std::time::Duration; use async_trait::async_trait; -use tokio::sync::Mutex; -use crate::prudp::socket::ExternalConnection; +use chrono::TimeDelta; +use macros::{rmc_proto, rmc_struct}; +use paste::paste; +use tokio::sync::{Mutex, Notify}; +use tokio::time::{sleep_until, Instant}; +use crate::prudp::socket::{ExternalConnection, SendingConnection}; use crate::rmc::structures::connection_data::ConnectionData; -pub trait RmcCallable{ - fn rmc_call(protocol_id: u8, method_id: u8, rest: Vec); -} +struct RmcConnection(SendingConnection, RmcResponseReceiver); -struct LocalRmcObjectWrapper(T); -impl LocalRmcObjectWrapper{ - pub fn new(object: T, conn: ExternalConnection) -> Self{ - unimplemented!() +struct RmcResponseReceiver(Notify, Mutex>>); + +impl RmcResponseReceiver{ + // returns none if timed out + pub async fn get_response_data(&self, proto: u16, method: u32) -> Option>{ + let mut end_wait_time = Instant::now(); + end_wait_time += Duration::from_secs(5); + + let sleep_fut = sleep_until(end_wait_time); + tokio::pin!(sleep_fut); + + loop { + let mut locked = self.1.lock().await; + + if let Some(v) = locked.remove(&(proto, method)){ + return Some(v); + } + + let notif_fut = self.0.notified(); + + drop(locked); + + tokio::select! { + _ = &mut sleep_fut => { + return None; + } + _ = notif_fut => { + continue; + } + } + } } } +pub trait RmcCallable{ + async fn rmc_call(&self, protocol_id: u16, method_id: u32, rest: Vec); +} + + +macro_rules! define_rmc_proto { + (proto $name:ident{ + $($protocol:path),* + }) => { + paste!{ + trait []: std::any::Any $( + [])* { + async fn rmc_call(&self, protocol_id: u16, method_id: u32, rest: Vec){ + match protocol_id{ + $( + []::PROTOCOL_ID => ]>::rmc_call_proto(self, method_id, rest).await, + )* + v => log::error!("invalid protocol called on rmc object {}", v) + } + } + } + } + }; +} + +trait RawNotif{ + async fn rmc_call_proto(&self, method_id: u32, rest: Vec){ + + } +} + +struct RawNotifInfo; +impl RawNotifInfo{ + const PROTOCOL_ID: u16 = 10; +} + +#[rmc_proto(1)] +pub trait Another{ + #[method_id(1)] + async fn test(); +} + +define_rmc_proto!{ + proto TestProto{ + Notif, + Another + } +} + + + +#[rmc_struct(TestProto)] +struct TestProtoImplementor{ + +} + +impl RawNotif for TestProtoImplementor{ + +} + From b8d2cd7b09a5fceff8814f7ec1d5d354e784f7cd Mon Sep 17 00:00:00 2001 From: DJMrTV Date: Sat, 8 Mar 2025 00:56:44 +0100 Subject: [PATCH 05/14] feat: stuff --- macros/src/lib.rs | 274 ++++++++++++++++++++++++++++++++++++++- src/prudp/socket.rs | 6 +- src/rmc/protocols/mod.rs | 41 +++++- 3 files changed, 308 insertions(+), 13 deletions(-) diff --git a/macros/src/lib.rs b/macros/src/lib.rs index bbd2923..1da5d58 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -3,13 +3,93 @@ extern crate proc_macro; use proc_macro2::{Ident, Literal, Span, TokenTree}; use proc_macro::TokenStream; use std::iter::FromIterator; -use syn::{parse_macro_input, DeriveInput, Data, PathSegment, TraitItem, FieldsNamed, Fields, Visibility, Type, TypePath, Path, ImplItem, ImplItemConst, Expr, ExprLit, Lit}; +use std::mem; +use syn::{parse_macro_input, DeriveInput, Data, PathSegment, TraitItem, FieldsNamed, Fields, Visibility, Type, TypePath, Path, ImplItem, ImplItemConst, Expr, ExprLit, Lit, TypeParamBound, TraitBound, TraitBoundModifier, LitInt, Token, FnArg, Receiver, PatType, Pat, TypeInfer, TypeReference, TraitItemFn, Signature, Block, Stmt, Local, LocalInit, LitStr, PathArguments}; use quote::{quote, ToTokens, TokenStreamExt}; -use syn::parse::ParseStream; +use syn::buffer::TokenBuffer; +use syn::parse::{Parse, ParseBuffer, ParseStream}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; +use syn::token::Comma; use syn::Visibility::Public; +fn self_referece_type() -> Type { + Type::Reference( + TypeReference { + and_token: Default::default(), + lifetime: None, + mutability: None, + elem: Box::new(Type::Path( + TypePath { + qself: None, + path: Path { + leading_colon: None, + segments: { + let mut punct = Punctuated::new(); + + punct.push_value(PathSegment{ + ident: Ident::new("Self", Span::call_site()), + arguments: PathArguments::None + }); + + punct + } + } + } + )) + } + ) +} + +struct ProtoInputParams{ + proto_num: LitInt, + properties: Option<(Token![,], Punctuated)> +} + +impl Parse for ProtoInputParams{ + fn parse(input: ParseStream) -> syn::Result { + let proto_num = input.parse()?; + + if let Some(seperator) = input.parse()?{ + let mut punctuated = Punctuated::new(); + loop { + punctuated.push_value( + input.parse()? + ); + if let Some(punct) = input.parse()? { + punctuated.push_punct(punct); + } else { + return Ok( + Self{ + proto_num, + properties: Some((seperator, punctuated)) + } + ) + } + } + } else { + Ok( + Self{ + proto_num, + properties: None + } + ) + } + } +} + +fn single_ident_path(ident: Ident) -> Path{ + Path{ + segments: { + let mut punc = Punctuated::new(); + punc.push(PathSegment::from(ident)); + punc + }, + leading_colon: None, + } +} + + /// Example of user-defined [derive mode macro][1] /// /// [1]: https://doc.rust-lang.org/reference/procedural-macros.html#derive-mode-macros @@ -167,7 +247,26 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn rmc_proto(attr: TokenStream, input: TokenStream) -> TokenStream{ - let mut proto_num = parse_macro_input!(attr as syn::LitInt); + + let mut params = parse_macro_input!(attr as ProtoInputParams); + + let ProtoInputParams{ + proto_num, + properties + } = params; + + let no_return_data = properties.is_some_and(|p| p.1.iter().any(|i|{ + i.to_string() == "NoReturn" + })); + + let param_err_return = match no_return_data{ + true => quote!{ + return; + }, + false => quote!{ + return Err(ErrorCode::Core_InvalidArgument); + } + }; let mut input = parse_macro_input!(input as syn::ItemTrait); @@ -175,7 +274,7 @@ pub fn rmc_proto(attr: TokenStream, input: TokenStream) -> TokenStream{ let info_struct_ident = Ident::new(&info_struct_ident, input.ident.span()); let raw_details_struct = syn::ItemStruct{ - vis: Visibility::Public(Default::default()), + vis: Public(Default::default()), struct_token: Default::default(), fields: Fields::Unit, semi_token: Some(Default::default()), @@ -235,9 +334,27 @@ pub fn rmc_proto(attr: TokenStream, input: TokenStream) -> TokenStream{ ] }; - let funcs = input.items.iter().filter_map(|v| if let TraitItem::Fn(v) = v {Some(v)} else { None }); + let mut raw_trait = input.clone(); + + raw_trait.ident = Ident::new(&format!("Raw{}",raw_trait.ident.to_string()), raw_trait.ident.span()); + raw_trait.colon_token = Some(Default::default()); + raw_trait.supertraits = { + let mut punct = Punctuated::new(); + punct.push(TypeParamBound::Trait(TraitBound{ + path: single_ident_path(input.ident.clone()), + lifetimes: None, + modifier: TraitBoundModifier::None, + paren_token: None + })); + punct + }; + + let mut functions: Vec<(LitInt, Ident)> = Vec::new(); + + let funcs = raw_trait.items.iter_mut().filter_map(|v| if let TraitItem::Fn(v) = v {Some(v)} else { None }); for func in funcs{ + if matches!(func.default, Some(_)){ return syn::Error::new(func.default.span(), "rmc methods may not have bodies").to_compile_error().into(); } @@ -248,13 +365,152 @@ pub fn rmc_proto(attr: TokenStream, input: TokenStream) -> TokenStream{ return syn::Error::new(span, "every function inside of an rmc protocol must have a method id").to_compile_error().into(); }; - todo!("generate raw impl") + let Ok(func_id): Result = attr.parse_args() else { + return syn::Error::new(Span::call_site(), "todo: put a propper error message here").to_compile_error().into(); + }; + + + if !func.sig.inputs.first().is_some_and(|v| matches!(v, FnArg::Receiver(Receiver{ + colon_token: None, + mutability: None, + reference: Some(_), + .. + }))){ + return syn::Error::new(func.sig.inputs.span(), "every protocol function must have a ` & self ` as its first parameter.").to_compile_error().into(); + } + + let old_ident = func.sig.ident.clone(); + + func.sig.ident = Ident::new(&format!("raw_{}", func.sig.ident), func.sig.ident.span()); + + let mut new_params: Punctuated<_,_> = Punctuated::new(); + + new_params.push_value(FnArg::Receiver(Receiver{ + attrs: vec![], + mutability: None, + ty: Box::new(self_referece_type()), + colon_token: None, + self_token: Default::default(), + reference: Some((Default::default(), None)) + })); + + new_params.push_punct(Comma::default()); +/* + new_params.push_value(FnArg::Typed(PatType{ + attrs: vec![], + pat: Box::new(Pat::Verbatim(quote! { method_id })), + colon_token: Default::default(), + ty: Box::new(Type::Verbatim(quote!{u32})) + })); + + new_params.push_punct(Comma::default());*/ + + new_params.push_value(FnArg::Typed(PatType{ + attrs: vec![], + pat: Box::new(Pat::Verbatim(quote! {data})), + colon_token: Default::default(), + ty: Box::new(Type::Verbatim(quote!{::std::vec::Vec})) + })); + + mem::swap(&mut new_params, &mut func.sig.inputs); + let old_params = new_params; + + + let mut inner_raw_tokens = quote!{ + let mut cursor = ::std::io::Cursor::new(data); + }; + + let mut call_params = quote!{}; + + + for param in old_params.iter().skip(1).filter_map(|v| if let FnArg::Typed(t) = v { + Some(t) + } else { + None + }){ + let param_name = &*param.pat; + let ty = &*param.ty; + + inner_raw_tokens.append_all(quote!{ + let Ok(#param_name) = <#ty as crate::rmc::structures::RmcSerialize>::deserialize(&mut cursor) else { + #param_err_return + }; + }); + + call_params.append_all(quote!{#param_name ,}) + } + + inner_raw_tokens.append_all(quote!{ + let retval = self.#old_ident(#call_params).await; + }); + + if !no_return_data{ + //let + + //inner_raw_tokens.append_all() + } + + + let braced = quote! { + { + #inner_raw_tokens + } + }; + + let braced = braced.into(); + + func.default = Some( + parse_macro_input!(braced as Block) + ); + + functions.push((func_id, func.sig.ident.clone())); } + let mut inner_match = quote!{}; + + for toks in functions.iter().map(|(lit, ident)|{ + quote! { + #lit => self.#ident(data).await, + } + }){ + inner_match.append_all(toks); + } + + if no_return_data{ + inner_match.append_all(quote!{ + _ => return + }) + } else { + // + inner_match.append_all(quote!{ + _ => return + }) + } + + raw_trait.items.push( + TraitItem::Verbatim( + quote!{ + async fn rmc_call_proto(&self, method_id: u32, data: Vec){ + match method_id{ + #inner_match + } + } + } + ) + ); + + + + let regular_trait_name = &input.ident; + let raw_trait_name = &raw_trait.ident; + quote!{ #input #raw_details_struct #raw_details_impl_block + #raw_trait + + impl #raw_trait_name for T{} }.into() } @@ -293,4 +549,10 @@ pub fn rmc_struct(attr: TokenStream, input: TokenStream) -> TokenStream{ }; out.into() +} + +#[proc_macro_attribute] +pub fn connection(_attr: TokenStream, input: TokenStream) -> TokenStream{ + // this attribute doesnt do anything by itself, see `rmc_struct` + input } \ No newline at end of file diff --git a/src/prudp/socket.rs b/src/prudp/socket.rs index 9c4db03..a0429c8 100644 --- a/src/prudp/socket.rs +++ b/src/prudp/socket.rs @@ -514,9 +514,9 @@ pub struct NewEncryptionPair { pub struct CommonConnection { pub user_id: u32, - session_id: u8, pub socket_addr: PRUDPSockAddr, - pub server_port: VirtualPort + pub server_port: VirtualPort, + session_id: u8, } struct InternalConnection { @@ -670,6 +670,8 @@ impl InternalSocket { .write_to(&mut vec) .expect("somehow failed to convert backet to bytes"); + println!("{}", hex::encode(&vec)); + self.socket .send_to(&vec, dest.regular_socket_addr) .await diff --git a/src/rmc/protocols/mod.rs b/src/rmc/protocols/mod.rs index 5025cb4..2603f8c 100644 --- a/src/rmc/protocols/mod.rs +++ b/src/rmc/protocols/mod.rs @@ -11,10 +11,11 @@ use tokio::sync::{Mutex, Notify}; use tokio::time::{sleep_until, Instant}; use crate::prudp::socket::{ExternalConnection, SendingConnection}; use crate::rmc::structures::connection_data::ConnectionData; +use crate::rmc::structures::matchmake::AutoMatchmakeParam; -struct RmcConnection(SendingConnection, RmcResponseReceiver); +pub struct RmcConnection(pub SendingConnection, pub RmcResponseReceiver); -struct RmcResponseReceiver(Notify, Mutex>>); +pub struct RmcResponseReceiver(Notify, Mutex>>); impl RmcResponseReceiver{ // returns none if timed out @@ -48,17 +49,29 @@ impl RmcResponseReceiver{ } } +pub trait RemoteObject{ + fn new(conn: RmcConnection) -> Self; +} + +impl RemoteObject for (){ + fn new(_: RmcConnection) -> Self {} +} + pub trait RmcCallable{ + //type Remote: RemoteObject; + //fn new_callable(remote: Self::Remote); async fn rmc_call(&self, protocol_id: u16, method_id: u32, rest: Vec); } + + macro_rules! define_rmc_proto { (proto $name:ident{ $($protocol:path),* }) => { paste!{ - trait []: std::any::Any $( + [])* { + trait []: std::any::Any $( + [] + $protocol)* { async fn rmc_call(&self, protocol_id: u16, method_id: u32, rest: Vec){ match protocol_id{ $( @@ -77,16 +90,21 @@ trait RawNotif{ } } +trait Notif{ + +} struct RawNotifInfo; impl RawNotifInfo{ const PROTOCOL_ID: u16 = 10; } -#[rmc_proto(1)] +pub trait ImplementRemoteCalls{} + +#[rmc_proto(1, NoReturn)] pub trait Another{ #[method_id(1)] - async fn test(); + async fn test(&self, thing: AutoMatchmakeParam); } define_rmc_proto!{ @@ -100,6 +118,12 @@ define_rmc_proto!{ #[rmc_struct(TestProto)] struct TestProtoImplementor{ + +} + + + +impl Notif for TestProtoImplementor{ } @@ -107,3 +131,10 @@ impl RawNotif for TestProtoImplementor{ } +impl Another for TestProtoImplementor{ + async fn test(&self, thing: AutoMatchmakeParam) { + + } +} + +impl ImplementRemoteCalls for TestProtoImplementor{} \ No newline at end of file From fa37331780050771ba67d842765fbf1c2e675ee3 Mon Sep 17 00:00:00 2001 From: DJMrTV Date: Sun, 23 Mar 2025 10:54:01 +0100 Subject: [PATCH 06/14] feat: stuff happend --- macros/src/lib.rs | 321 +++++++------------------------ macros/src/protos.rs | 173 +++++++++++++++++ src/grpc/mod.rs | 4 + src/main.rs | 28 ++- src/rmc/protocols/auth.rs | 34 ++++ src/rmc/protocols/mod.rs | 92 +++------ src/rmc/response.rs | 74 +++++-- src/rmc/structures/buffer.rs | 2 + src/rmc/structures/list.rs | 3 + src/rmc/structures/mod.rs | 13 +- src/rmc/structures/primitives.rs | 108 +++++++++++ 11 files changed, 510 insertions(+), 342 deletions(-) create mode 100644 macros/src/protos.rs create mode 100644 src/rmc/protocols/auth.rs diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 1da5d58..eb3d5da 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,10 +1,12 @@ +mod protos; + extern crate proc_macro; use proc_macro2::{Ident, Literal, Span, TokenTree}; use proc_macro::TokenStream; use std::iter::FromIterator; use std::mem; -use syn::{parse_macro_input, DeriveInput, Data, PathSegment, TraitItem, FieldsNamed, Fields, Visibility, Type, TypePath, Path, ImplItem, ImplItemConst, Expr, ExprLit, Lit, TypeParamBound, TraitBound, TraitBoundModifier, LitInt, Token, FnArg, Receiver, PatType, Pat, TypeInfer, TypeReference, TraitItemFn, Signature, Block, Stmt, Local, LocalInit, LitStr, PathArguments}; +use syn::{parse_macro_input, DeriveInput, Data, PathSegment, TraitItem, FieldsNamed, Fields, Visibility, Type, TypePath, Path, ImplItem, ImplItemConst, Expr, ExprLit, Lit, TypeParamBound, TraitBound, TraitBoundModifier, LitInt, Token, FnArg, Receiver, PatType, Pat, TypeInfer, TypeReference, TraitItemFn, Signature, Block, Stmt, Local, LocalInit, LitStr, PathArguments, ReturnType}; use quote::{quote, ToTokens, TokenStreamExt}; use syn::buffer::TokenBuffer; use syn::parse::{Parse, ParseBuffer, ParseStream}; @@ -12,6 +14,7 @@ use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::token::Comma; use syn::Visibility::Public; +use crate::protos::{ProtoMethodData, RmcProtocolData}; fn self_referece_type() -> Type { Type::Reference( @@ -90,9 +93,6 @@ fn single_ident_path(ident: Ident) -> Path{ } -/// Example of user-defined [derive mode macro][1] -/// -/// [1]: https://doc.rust-lang.org/reference/procedural-macros.html#derive-mode-macros #[proc_macro_derive(RmcSerialize, attributes(extends, rmc_struct))] pub fn rmc_serialize(input: TokenStream) -> TokenStream { let derive_input = parse_macro_input!(input as DeriveInput); @@ -245,6 +245,30 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { tokens.into() } +/// Macro to automatically generate code to use a specific trait as an rmc protocol for calling to +/// remote objects or accepting incoming remote requests. +/// This is needed in order to be able to use this as part of an rmc server interface. +/// +/// The protocol id which is needed to be specified is specified as a parameter to this attribute. +/// +/// You will also need to assign each function inside the trait a method id by using the +/// [`macro@method_id`] attribute. +/// +/// You can also specify to have the protocol to be non-returning by adding a second parameter to +/// the attribute which is just `NoReturn` e.g. ` #[rmc_proto(1, NoReturn)]` +/// +/// Example +/// ``` +/// // this rmc protocol has protocol id 1 +/// use macros::rmc_proto; +/// +/// #[rmc_proto(1)] +/// trait ExampleProtocol{ +/// // this defines an rmc method with id 1 +/// #[rmc_method(1)] +/// async fn hello_world_method(&self, name: String) -> Result; +/// } +/// ``` #[proc_macro_attribute] pub fn rmc_proto(attr: TokenStream, input: TokenStream) -> TokenStream{ @@ -259,263 +283,63 @@ pub fn rmc_proto(attr: TokenStream, input: TokenStream) -> TokenStream{ i.to_string() == "NoReturn" })); - let param_err_return = match no_return_data{ - true => quote!{ - return; - }, - false => quote!{ - return Err(ErrorCode::Core_InvalidArgument); - } - }; - let mut input = parse_macro_input!(input as syn::ItemTrait); - let info_struct_ident = format!("Raw{}Info",input.ident.to_string()); - let info_struct_ident = Ident::new(&info_struct_ident, input.ident.span()); - - let raw_details_struct = syn::ItemStruct{ - vis: Public(Default::default()), - struct_token: Default::default(), - fields: Fields::Unit, - semi_token: Some(Default::default()), - ident: info_struct_ident.clone(), - attrs: vec![], - generics: Default::default() - }; - - let raw_details_impl_block = syn::ItemImpl{ - impl_token: Default::default(), - generics: Default::default(), - attrs: vec![], - brace_token: Default::default(), - defaultness: None, - trait_: None, - self_ty: Box::new(Type::Path(TypePath{ - qself: None, - path: Path{ - segments: { - let mut punc = Punctuated::new(); - punc.push(PathSegment::from(info_struct_ident)); - punc - }, - leading_colon: None, - } - })), - unsafety: None, - items: vec![ - ImplItem::Const( - ImplItemConst{ - defaultness: None, - semi_token: Default::default(), - attrs: vec![], - generics: Default::default(), - ident: Ident::new("PROTOCOL_ID", Span::call_site()), - vis: Public(Default::default()), - colon_token: Default::default(), - const_token: Default::default(), - eq_token: Default::default(), - expr: Expr::Lit(ExprLit{ - attrs: vec![], - lit: Lit::Int(proto_num), - }), - ty: Type::Path(TypePath{ - qself: None, - path: Path{ - segments: { - let mut punc = Punctuated::new(); - punc.push(PathSegment::from(Ident::new("u16", Span::call_site()))); - punc - }, - leading_colon: None, - } - }) - } - ) - ] - }; - - let mut raw_trait = input.clone(); - - raw_trait.ident = Ident::new(&format!("Raw{}",raw_trait.ident.to_string()), raw_trait.ident.span()); - raw_trait.colon_token = Some(Default::default()); - raw_trait.supertraits = { - let mut punct = Punctuated::new(); - punct.push(TypeParamBound::Trait(TraitBound{ - path: single_ident_path(input.ident.clone()), - lifetimes: None, - modifier: TraitBoundModifier::None, - paren_token: None - })); - punct - }; - - let mut functions: Vec<(LitInt, Ident)> = Vec::new(); - - let funcs = raw_trait.items.iter_mut().filter_map(|v| if let TraitItem::Fn(v) = v {Some(v)} else { None }); - - for func in funcs{ - - if matches!(func.default, Some(_)){ - return syn::Error::new(func.default.span(), "rmc methods may not have bodies").to_compile_error().into(); - } - - let Some(attr) = func.attrs.iter() - .find(|a| a.path().segments.last().is_some_and(|s| s.ident.to_string() == "method_id")) else { - let span = func.sig.asyncness.span().join(func.semi_token.unwrap().span()).unwrap_or(func.sig.span()); - return syn::Error::new(span, "every function inside of an rmc protocol must have a method id").to_compile_error().into(); - }; - - let Ok(func_id): Result = attr.parse_args() else { - return syn::Error::new(Span::call_site(), "todo: put a propper error message here").to_compile_error().into(); - }; - - - if !func.sig.inputs.first().is_some_and(|v| matches!(v, FnArg::Receiver(Receiver{ - colon_token: None, - mutability: None, - reference: Some(_), - .. - }))){ - return syn::Error::new(func.sig.inputs.span(), "every protocol function must have a ` & self ` as its first parameter.").to_compile_error().into(); - } - - let old_ident = func.sig.ident.clone(); - - func.sig.ident = Ident::new(&format!("raw_{}", func.sig.ident), func.sig.ident.span()); - - let mut new_params: Punctuated<_,_> = Punctuated::new(); - - new_params.push_value(FnArg::Receiver(Receiver{ - attrs: vec![], - mutability: None, - ty: Box::new(self_referece_type()), - colon_token: None, - self_token: Default::default(), - reference: Some((Default::default(), None)) - })); - - new_params.push_punct(Comma::default()); -/* - new_params.push_value(FnArg::Typed(PatType{ - attrs: vec![], - pat: Box::new(Pat::Verbatim(quote! { method_id })), - colon_token: Default::default(), - ty: Box::new(Type::Verbatim(quote!{u32})) - })); - - new_params.push_punct(Comma::default());*/ - - new_params.push_value(FnArg::Typed(PatType{ - attrs: vec![], - pat: Box::new(Pat::Verbatim(quote! {data})), - colon_token: Default::default(), - ty: Box::new(Type::Verbatim(quote!{::std::vec::Vec})) - })); - - mem::swap(&mut new_params, &mut func.sig.inputs); - let old_params = new_params; - - - let mut inner_raw_tokens = quote!{ - let mut cursor = ::std::io::Cursor::new(data); - }; - - let mut call_params = quote!{}; - - - for param in old_params.iter().skip(1).filter_map(|v| if let FnArg::Typed(t) = v { - Some(t) - } else { - None - }){ - let param_name = &*param.pat; - let ty = &*param.ty; - - inner_raw_tokens.append_all(quote!{ - let Ok(#param_name) = <#ty as crate::rmc::structures::RmcSerialize>::deserialize(&mut cursor) else { - #param_err_return + // gigantic ass struct initializer (to summarize this gets all of the data) + let raw_data = RmcProtocolData{ + has_returns: !no_return_data, + name: input.ident.clone(), + id: proto_num, + methods: input + .items + .iter() + .filter_map(|v| match v{ TraitItem::Fn(v) => Some(v), _ => None }) + .map(|func|{ + let Some(attr) = func.attrs.iter() + .find(|a| a.path().segments.last().is_some_and(|s| s.ident.to_string() == "method_id")) else { + panic!( "every function inside of an rmc protocol must have a method id"); }; - }); - call_params.append_all(quote!{#param_name ,}) - } + let Ok(id): Result = attr.parse_args() else { + panic!("todo: put a propper error message here"); + }; - inner_raw_tokens.append_all(quote!{ - let retval = self.#old_ident(#call_params).await; - }); + let funcs = func + .sig + .inputs + .iter() + .skip(1) + .map(|f| { + let FnArg::Typed(t) = f else { + panic!("what"); + }; + let Pat::Ident(i) = &*t.pat else { + panic!("unable to handle non identifier patterns as parameter bindings"); + }; - if !no_return_data{ - //let + (i.ident.clone(), t.ty.as_ref().clone()) + }).collect(); - //inner_raw_tokens.append_all() - } - - - let braced = quote! { - { - #inner_raw_tokens - } - }; - - let braced = braced.into(); - - func.default = Some( - parse_macro_input!(braced as Block) - ); - - functions.push((func_id, func.sig.ident.clone())); - } - - let mut inner_match = quote!{}; - - for toks in functions.iter().map(|(lit, ident)|{ - quote! { - #lit => self.#ident(data).await, - } - }){ - inner_match.append_all(toks); - } - - if no_return_data{ - inner_match.append_all(quote!{ - _ => return - }) - } else { - // - inner_match.append_all(quote!{ - _ => return - }) - } - - raw_trait.items.push( - TraitItem::Verbatim( - quote!{ - async fn rmc_call_proto(&self, method_id: u32, data: Vec){ - match method_id{ - #inner_match - } + ProtoMethodData{ + id, + name: func.sig.ident.clone(), + parameters: funcs } - } - ) - ); + }).collect() - - - let regular_trait_name = &input.ident; - let raw_trait_name = &raw_trait.ident; + }; quote!{ #input - #raw_details_struct - #raw_details_impl_block - #raw_trait - - impl #raw_trait_name for T{} + #raw_data }.into() - } - +/// Used to specify the method id of methods when making rmc protocols. +/// See [`macro@rmc_proto`] for further details. +/// +/// Note: This attribute doesn't do anything by itself and just returns the thing it was attached to +/// unchanged. #[proc_macro_attribute] pub fn method_id(_attr: TokenStream, input: TokenStream) -> TokenStream{ // this attribute doesnt do anything by itself, see `rmc_proto` @@ -523,6 +347,7 @@ pub fn method_id(_attr: TokenStream, input: TokenStream) -> TokenStream{ } + #[proc_macro_attribute] pub fn rmc_struct(attr: TokenStream, input: TokenStream) -> TokenStream{ let mut type_data = parse_macro_input!(input as DeriveInput); @@ -542,8 +367,8 @@ pub fn rmc_struct(attr: TokenStream, input: TokenStream) -> TokenStream{ } impl crate::rmc::protocols::RmcCallable for #struct_name{ - async fn rmc_call(&self, protocol_id: u16, method_id: u32, rest: Vec){ - ::rmc_call(self, protocol_id, method_id, rest).await; + async fn rmc_call(&self, remote_response_connection: &crate::prudp::socket::SendingConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec){ + ::rmc_call(self, remote_response_connection, protocol_id, method_id, call_id, rest).await; } } }; diff --git a/macros/src/protos.rs b/macros/src/protos.rs new file mode 100644 index 0000000..cc0e5cd --- /dev/null +++ b/macros/src/protos.rs @@ -0,0 +1,173 @@ +use proc_macro2::{Ident, Span, TokenStream, TokenTree}; +use quote::{quote, ToTokens}; +use syn::{LitInt, Token, Type}; +use syn::token::{Brace, Paren, Semi}; + +pub struct ProtoMethodData{ + pub id: LitInt, + pub name: Ident, + pub parameters: Vec<(Ident, Type)> +} + + +/// This is a representation of the code generated by `rmc_proto` it serves to split the logic of +/// acquiring data from the actual generation to tidy up the process into first getting then +/// generating. +/// +/// Use the [`ToTokens`] trait to generate the actual code. +pub struct RmcProtocolData{ + pub has_returns: bool, + pub id: LitInt, + pub name: Ident, + pub methods: Vec +} + +impl ToTokens for RmcProtocolData{ + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self{ + has_returns, + name, + id, + methods + } = self; + + // this gives us the name which the identifier of the corresponding Raw trait + let raw_name = Ident::new(&format!("Raw{}", name), name.span()); + + + // boilerplate tokens which all raw traits need + quote!{ + #[doc(hidden)] + pub trait #raw_name: #name + }.to_tokens(tokens); + + // generate the body of the raw protocol trait + Brace::default().surround(tokens, |tokens|{ + //generate each raw method + for method in methods{ + let ProtoMethodData { + name, + parameters, + .. + } = method; + + let raw_name = Ident::new(&format!("raw_{}", name), name.span()); + quote!{ + async fn #raw_name + }.to_tokens(tokens); + + Paren::default().surround(tokens, |tokens|{ + quote!{ &self, data: ::std::vec::Vec }.to_tokens(tokens); + }); + + quote!{ + -> ::core::result::Result, ErrorCode> + }.to_tokens(tokens); + + Brace::default().surround(tokens, |tokens|{ + quote! { let mut cursor = ::std::io::Cursor::new(data); }.to_tokens(tokens); + + for (param_name, param_type) in parameters{ + quote!{ + let Ok(#param_name) = + <#param_type as crate::rmc::structures::RmcSerialize>::deserialize( + &mut cursor + ) else { + return Err(ErrorCode::Core_InvalidArgument); + }; + }.to_tokens(tokens) + } + + quote!{ + let retval = self.#name + }.to_tokens(tokens); + + Paren::default().surround(tokens, |tokens|{ + for (paren_name, _) in parameters{ + quote!{#paren_name,}.to_tokens(tokens); + } + }); + + quote!{ + .await; + }.to_tokens(tokens); + + if *has_returns{ + quote!{ + let retval = retval?; + let mut vec = Vec::new(); + crate::rmc::structures::RmcSerialize::serialize(&retval, &mut vec).ok(); + Ok(vec) + }.to_tokens(tokens); + } + }) + } + + quote!{ + async fn rmc_call_proto( + &self, + remote_response_connection: &crate::prudp::socket::SendingConnection, + method_id: u32, + call_id: u32, + data: Vec, + ) + }.to_tokens(tokens); + + Brace::default().surround(tokens, |tokens|{ + quote! { + let ret = match method_id + }.to_tokens(tokens); + + Brace::default().surround(tokens, |tokens|{ + for method in methods{ + let ProtoMethodData{ + id, + name, + .. + } = method; + + let raw_name = Ident::new(&format!("raw_{}", name), name.span()); + + quote!{ + #id => self.#raw_name(data).await, + }.to_tokens(tokens); + } + quote!{ + _ => Err(crate::rmc::response::ErrorCode::Core_NotImplemented) + }.to_tokens(tokens); + }); + + Semi::default().to_tokens(tokens); + + if *has_returns{ + quote!{ + crate::rmc::response::send_result( + remote_response_connection, + ret, + #id, + method_id, + call_id, + ).await + }.to_tokens(tokens); + } + }); + }); + + quote!{ + impl RawAuth for T{} + }.to_tokens(tokens); + + let raw_info_name = Ident::new(&format!("Raw{}Info", name), Span::call_site()); + + + quote!{ + #[doc(hidden)] + pub struct #raw_info_name; + + impl #raw_info_name { + pub const PROTOCOL_ID: u16 = #id; + } + }.to_tokens(tokens); + } +} + diff --git a/src/grpc/mod.rs b/src/grpc/mod.rs index d1a4342..2ca742e 100644 --- a/src/grpc/mod.rs +++ b/src/grpc/mod.rs @@ -1,3 +1,7 @@ +//! Legacy grpc communication server for being able to use this with pretendos infrastructure +//! before account rs is finished. +//! +//! This WILL be deprecated as soon as account rs is in a stable state. use tonic::{Request, Status}; type InterceptorFunc = Box<(dyn Fn(Request<()>) -> Result, Status> + Send)>; diff --git a/src/main.rs b/src/main.rs index bb44bc5..cb2d033 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,24 +1,24 @@ #![allow(dead_code)] +#![warn(missing_docs)] + +//! # Splatoon RNEX server +//! +//! This server still includes the code for rnex itself as this is the first rnex server and thus +//! also the first and only current usage of rnex, expect this and rnex to be split into seperate +//! repos soon. use std::{env, fs}; -use std::collections::BTreeMap; use std::fs::File; use std::net::{Ipv4Addr, SocketAddrV4}; -use std::sync::Arc; use chrono::{Local, SecondsFormat}; use log::info; use once_cell::sync::Lazy; -use rc4::{KeyInit, Rc4, StreamCipher}; -use rc4::consts::U5; use simplelog::{ColorChoice, CombinedLogger, Config, LevelFilter, TerminalMode, TermLogger, WriteLogger}; -use tokio::sync::RwLock; -use tokio::task::JoinHandle; use crate::nex::account::Account; -use crate::prudp::socket::{EncryptionPair, Unsecure}; +use crate::prudp::socket::Unsecure; use crate::prudp::packet::{VirtualPort}; use crate::prudp::router::Router; use crate::prudp::sockaddr::PRUDPSockAddr; -use crate::prudp::station_url::Type::PRUDP; mod endianness; mod prudp; @@ -234,6 +234,18 @@ async fn start_secure_server() -> SecureServer{ socket, } }*/ +/* +define_rmc_proto!( + proto AuthClientProtocol{ + Auth + } +);*/ + + +//#[rmc_struct(AuthClientProtocol)] +struct AuthClient{ + +} async fn start_servers(){ diff --git a/src/rmc/protocols/auth.rs b/src/rmc/protocols/auth.rs new file mode 100644 index 0000000..5aa84f3 --- /dev/null +++ b/src/rmc/protocols/auth.rs @@ -0,0 +1,34 @@ +use crate::rmc::response::ErrorCode; +use crate::rmc::structures::any::Any; +use crate::rmc::structures::connection_data::ConnectionData; +use crate::rmc::structures::qresult::QResult; +use macros::{method_id, rmc_proto}; + + +/// This is the representation for `Ticket Granting`(for details see the +/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) +#[rmc_proto(10)] +pub trait Auth { + #[method_id(1)] + async fn login(&self, name: String) -> Result<(), ErrorCode>; + + #[method_id(2)] + async fn login_ex( + &self, + name: String, + extra_data: Any, + ) -> Result<(QResult, u32, Vec, ConnectionData, String), ErrorCode>; + + #[method_id(3)] + async fn request_ticket( + &self, + source_pid: u32, + destination_pid: u32, + ) -> Result<(QResult, Vec), ErrorCode>; + + #[method_id(4)] + async fn get_pid(&self, username: String) -> Result; + + #[method_id(5)] + async fn get_name(&self, pid: u32) -> Result; +} diff --git a/src/rmc/protocols/mod.rs b/src/rmc/protocols/mod.rs index 2603f8c..74c020c 100644 --- a/src/rmc/protocols/mod.rs +++ b/src/rmc/protocols/mod.rs @@ -1,3 +1,7 @@ +#![allow(async_fn_in_trait)] + +pub mod auth; + use macros::method_id; use std::collections::HashMap; use std::ops::Add; @@ -10,16 +14,18 @@ use paste::paste; use tokio::sync::{Mutex, Notify}; use tokio::time::{sleep_until, Instant}; use crate::prudp::socket::{ExternalConnection, SendingConnection}; +use crate::rmc::response::ErrorCode; use crate::rmc::structures::connection_data::ConnectionData; +use crate::rmc::structures::Error; use crate::rmc::structures::matchmake::AutoMatchmakeParam; pub struct RmcConnection(pub SendingConnection, pub RmcResponseReceiver); -pub struct RmcResponseReceiver(Notify, Mutex>>); +pub struct RmcResponseReceiver(Notify, Mutex>>); impl RmcResponseReceiver{ // returns none if timed out - pub async fn get_response_data(&self, proto: u16, method: u32) -> Option>{ + pub async fn get_response_data(&self, call_id: u32) -> Option>{ let mut end_wait_time = Instant::now(); end_wait_time += Duration::from_secs(5); @@ -29,13 +35,15 @@ impl RmcResponseReceiver{ loop { let mut locked = self.1.lock().await; - if let Some(v) = locked.remove(&(proto, method)){ + if let Some(v) = locked.remove(&call_id){ return Some(v); } + drop(locked); + + let notif_fut = self.0.notified(); - drop(locked); tokio::select! { _ = &mut sleep_fut => { @@ -49,6 +57,10 @@ impl RmcResponseReceiver{ } } +pub trait HasRmcConnection{ + fn get_response_receiver(&self) -> &RmcConnection; +} + pub trait RemoteObject{ fn new(conn: RmcConnection) -> Self; } @@ -57,84 +69,36 @@ impl RemoteObject for (){ fn new(_: RmcConnection) -> Self {} } + + pub trait RmcCallable{ //type Remote: RemoteObject; //fn new_callable(remote: Self::Remote); - async fn rmc_call(&self, protocol_id: u16, method_id: u32, rest: Vec); + async fn rmc_call(&self, responder: &SendingConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec); } - +#[macro_export] macro_rules! define_rmc_proto { (proto $name:ident{ $($protocol:path),* }) => { - paste!{ + paste::paste!{ trait []: std::any::Any $( + [] + $protocol)* { - async fn rmc_call(&self, protocol_id: u16, method_id: u32, rest: Vec){ + async fn rmc_call(&self, remote_response_connection: &crate::prudp::socket::SendingConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec){ match protocol_id{ $( - []::PROTOCOL_ID => ]>::rmc_call_proto(self, method_id, rest).await, + []::PROTOCOL_ID => ]>::rmc_call_proto(self, remote_response_connection, method_id, call_id, rest).await, )* v => log::error!("invalid protocol called on rmc object {}", v) } } } + + struct [](crate::rmc::protocols::RmcConnection); + + } }; -} - -trait RawNotif{ - async fn rmc_call_proto(&self, method_id: u32, rest: Vec){ - - } -} -trait Notif{ - -} - -struct RawNotifInfo; -impl RawNotifInfo{ - const PROTOCOL_ID: u16 = 10; -} - -pub trait ImplementRemoteCalls{} - -#[rmc_proto(1, NoReturn)] -pub trait Another{ - #[method_id(1)] - async fn test(&self, thing: AutoMatchmakeParam); -} - -define_rmc_proto!{ - proto TestProto{ - Notif, - Another - } -} - - - -#[rmc_struct(TestProto)] -struct TestProtoImplementor{ - -} - - - -impl Notif for TestProtoImplementor{ - -} - -impl RawNotif for TestProtoImplementor{ - -} - -impl Another for TestProtoImplementor{ - async fn test(&self, thing: AutoMatchmakeParam) { - - } -} - -impl ImplementRemoteCalls for TestProtoImplementor{} \ No newline at end of file +} \ No newline at end of file diff --git a/src/rmc/response.rs b/src/rmc/response.rs index bbbfb13..ece1486 100644 --- a/src/rmc/response.rs +++ b/src/rmc/response.rs @@ -6,41 +6,42 @@ use crate::prudp::packet::{PRUDPPacket}; use crate::prudp::packet::flags::{NEED_ACK, RELIABLE}; use crate::prudp::packet::PacketOption::FragmentId; use crate::prudp::packet::types::DATA; -use crate::prudp::socket::ExternalConnection; +use crate::prudp::socket::{ExternalConnection, SendingConnection}; use crate::rmc::structures::qresult::ERROR_MASK; +use crate::rmc::structures::RmcSerialize; use crate::web::DirectionalData::{Incoming, Outgoing}; use crate::web::WEB_DATA; pub enum RMCResponseResult { - Success{ + Success { call_id: u32, method_id: u32, data: Vec, }, - Error{ + Error { error_code: ErrorCode, call_id: u32, - } + }, } pub struct RMCResponse { pub protocol_id: u8, - pub response_result: RMCResponseResult + pub response_result: RMCResponseResult, } impl RMCResponse { - pub fn to_data(self) -> Vec{ + pub fn to_data(self) -> Vec { generate_response(self.protocol_id, self.response_result).expect("failed to generate response") } } -pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Result>{ - let size = 1 + 1 + match &response{ +pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Result> { + let size = 1 + 1 + match &response { RMCResponseResult::Success { data, .. } => 4 + 4 + data.len(), - RMCResponseResult::Error{..} => 4 + 4, + RMCResponseResult::Error { .. } => 4 + 4, }; let mut data_out = Vec::with_capacity(size + 4); @@ -50,7 +51,7 @@ pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Re data_out.write_all(bytes_of(&u32_size))?; data_out.push(protocol_id); - match response{ + match response { RMCResponseResult::Success { call_id, method_id, @@ -61,7 +62,7 @@ pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Re let ored_method_id = method_id | 0x8000; data_out.write_all(bytes_of(&ored_method_id))?; data_out.write_all(&data)?; - }, + } RMCResponseResult::Error { call_id, error_code @@ -78,10 +79,44 @@ pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Re Ok(data_out) } -pub async fn send_response(original_packet: &PRUDPPacket, connection: &mut ExternalConnection, rmcresponse: RMCResponse){ + +pub async fn send_result( + connection: &SendingConnection, + result: Result, ErrorCode>, + protocol_id: u8, + method_id: u32, + call_id: u32, +) { + let response_result = match result { + Ok(v) => RMCResponseResult::Success { + call_id, + method_id, + data: { + let mut vec = Vec::new(); + v.serialize(&mut vec).expect("serialization error"); + vec + } + }, + Err(e) => + RMCResponseResult::Error { + call_id, + error_code: e.into() + } + }; + + let response = RMCResponse{ + response_result, + protocol_id + }; + + send_response(connection, response).await +} + +pub async fn send_response(connection: &SendingConnection, rmcresponse: RMCResponse) { connection.send(rmcresponse.to_data()).await; } + //taken from kinnays error list directly #[allow(nonstandard_style)] #[repr(u32)] @@ -356,25 +391,25 @@ pub enum ErrorCode { Custom_Unknown = 0x00740001, Ess_Unknown = 0x00750001, Ess_GameSessionError = 0x00750002, - Ess_GameSessionMaintenance = 0x00750003 + Ess_GameSessionMaintenance = 0x00750003, } -impl Into for ErrorCode { +impl Into for ErrorCode { fn into(self) -> u32 { - unsafe{ transmute(self) } + unsafe { transmute(self) } } } #[cfg(test)] -mod test{ +mod test { use hmac::digest::consts::U5; use hmac::digest::KeyInit; use rc4::{Rc4, StreamCipher}; use crate::rmc::response::ErrorCode; #[test] - fn test(){ - let mut data_orig = [0,1,2,3,4,5,6,7,8,9,69,4,20]; + fn test() { + let mut data_orig = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 69, 4, 20]; let mut data = data_orig; let mut rc4: Rc4 = @@ -390,11 +425,10 @@ mod test{ rc4.apply_keystream(&mut data); assert_eq!(data_orig, data); - } #[test] - fn test_enum_equivilance(){ + fn test_enum_equivilance() { let val: u32 = ErrorCode::Core_Unknown.into(); assert_eq!(val, 0x00010001) } diff --git a/src/rmc/structures/buffer.rs b/src/rmc/structures/buffer.rs index aea5a45..5370cf5 100644 --- a/src/rmc/structures/buffer.rs +++ b/src/rmc/structures/buffer.rs @@ -1,6 +1,8 @@ use std::io::{Read, Write}; use crate::rmc::structures::RmcSerialize; + + impl<'a> RmcSerialize for &'a [u8]{ fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { let u32_size = self.len() as u32; diff --git a/src/rmc/structures/list.rs b/src/rmc/structures/list.rs index 7f6c97c..6f6b456 100644 --- a/src/rmc/structures/list.rs +++ b/src/rmc/structures/list.rs @@ -4,6 +4,9 @@ use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; use crate::rmc::structures::RmcSerialize; + +// this is also for implementing `Buffer` this is tecnically not the same as its handled internaly +// probably but as it has the same mapping it doesn't matter and simplifies things impl RmcSerialize for Vec{ fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { let u32_len = self.len() as u32; diff --git a/src/rmc/structures/mod.rs b/src/rmc/structures/mod.rs index 667af6e..a9f9ab6 100644 --- a/src/rmc/structures/mod.rs +++ b/src/rmc/structures/mod.rs @@ -17,7 +17,7 @@ pub enum Error{ VersionMismatch(u8), } -pub(crate) type Result = std::result::Result; +pub type Result = std::result::Result; pub mod string; pub mod any; @@ -30,9 +30,18 @@ pub mod qbuffer; pub mod primitives; pub mod matchmake; pub mod variant; -mod ranking; +pub mod ranking; pub trait RmcSerialize: Sized{ fn serialize(&self, writer: &mut dyn Write) -> Result<()>; fn deserialize(reader: &mut dyn Read) -> Result; +} + +impl RmcSerialize for (){ + fn serialize(&self, writer: &mut dyn Write) -> Result<()> { + Ok(()) + } + fn deserialize(reader: &mut dyn Read) -> Result { + Ok(()) + } } \ No newline at end of file diff --git a/src/rmc/structures/primitives.rs b/src/rmc/structures/primitives.rs index bd08da8..e8855f0 100644 --- a/src/rmc/structures/primitives.rs +++ b/src/rmc/structures/primitives.rs @@ -91,4 +91,112 @@ impl RmcSerialize for (T, U){ Ok((first, second)) } +} + +impl RmcSerialize for (T, U, V){ + fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { + self.0.serialize(writer)?; + self.1.serialize(writer)?; + self.2.serialize(writer)?; + Ok(()) + } + + fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { + let first = T::deserialize(reader)?; + let second = U::deserialize(reader)?; + let third = V::deserialize(reader)?; + + Ok((first, second, third)) + } +} + +impl RmcSerialize for (T, U, V, W){ + fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { + self.0.serialize(writer)?; + self.1.serialize(writer)?; + self.2.serialize(writer)?; + self.3.serialize(writer)?; + Ok(()) + } + + fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { + let first = T::deserialize(reader)?; + let second = U::deserialize(reader)?; + let third = V::deserialize(reader)?; + let fourth = W::deserialize(reader)?; + + Ok((first, second, third, fourth)) + } +} + +impl RmcSerialize for (T, U, V, W, X){ + fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { + self.0.serialize(writer)?; + self.1.serialize(writer)?; + self.2.serialize(writer)?; + self.3.serialize(writer)?; + self.4.serialize(writer)?; + + Ok(()) + } + + fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { + let first = T::deserialize(reader)?; + let second = U::deserialize(reader)?; + let third = V::deserialize(reader)?; + let fourth = W::deserialize(reader)?; + let fifth = X::deserialize(reader)?; + + Ok((first, second, third, fourth, fifth)) + } +} + +impl RmcSerialize for (T, U, V, W, X, Y){ + fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { + self.0.serialize(writer)?; + self.1.serialize(writer)?; + self.2.serialize(writer)?; + self.3.serialize(writer)?; + self.4.serialize(writer)?; + self.5.serialize(writer)?; + + Ok(()) + } + + fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { + let first = T::deserialize(reader)?; + let second = U::deserialize(reader)?; + let third = V::deserialize(reader)?; + let fourth = W::deserialize(reader)?; + let fifth = X::deserialize(reader)?; + let sixth = Y::deserialize(reader)?; + + Ok((first, second, third, fourth, fifth, sixth)) + } +} + +impl RmcSerialize for (T, U, V, W, X, Y, Z){ + fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { + self.0.serialize(writer)?; + self.1.serialize(writer)?; + self.2.serialize(writer)?; + self.3.serialize(writer)?; + self.4.serialize(writer)?; + self.5.serialize(writer)?; + self.6.serialize(writer)?; + + Ok(()) + } + + fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { + let first = T::deserialize(reader)?; + let second = U::deserialize(reader)?; + let third = V::deserialize(reader)?; + let fourth = W::deserialize(reader)?; + let fifth = X::deserialize(reader)?; + let sixth = Y::deserialize(reader)?; + let seventh = Z::deserialize(reader)?; + + Ok((first, second, third, fourth, fifth, sixth, seventh)) + } } \ No newline at end of file From a7c36c39efc1e5d897602c9c04888bece95ac854 Mon Sep 17 00:00:00 2001 From: DJMrTV Date: Mon, 24 Mar 2025 23:31:25 +0100 Subject: [PATCH 07/14] feat: almost done with rework communication with remotes now works --- Cargo.lock | 54 +++--- Cargo.toml | 1 + macros/Cargo.toml | 1 + macros/src/lib.rs | 3 +- macros/src/protos.rs | 128 ++++++++++++- src/main.rs | 210 ++++++++++++---------- src/result.rs | 23 +++ src/rmc/protocols/auth.rs | 13 ++ src/rmc/protocols/mod.rs | 247 ++++++++++++++++++++++---- src/rmc/response.rs | 70 +++++++- src/rmc/structures/any.rs | 15 +- src/rmc/structures/connection_data.rs | 1 + src/rmc/structures/qresult.rs | 2 +- src/versions.rs | 97 ++++++++++ 14 files changed, 703 insertions(+), 162 deletions(-) create mode 100644 src/result.rs create mode 100644 src/versions.rs diff --git a/Cargo.lock b/Cargo.lock index a8ac40e..7fabb1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -614,17 +614,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.0-rc.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a78f88e84d239c7f2619ae8b091603c26208e1cb322571f5a29d6806f56ee5e" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", - "js-sys", "libc", - "rustix", - "wasi 0.13.3+wasi-0.2.2", - "wasm-bindgen", - "windows-targets 0.52.6", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -1051,6 +1048,7 @@ version = "0.0.0" dependencies = [ "proc-macro2", "quote", + "rand 0.9.0", "syn 2.0.98", ] @@ -1425,6 +1423,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "rand" version = "0.8.5" @@ -1438,12 +1442,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.0-beta.3" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fccbfebb3972a41a31c605a59207d9fba5489b9a87d9d87024cb6df73a32ec7" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ - "rand_chacha 0.9.0-beta.1", - "rand_core 0.9.0-beta.1", + "rand_chacha 0.9.0", + "rand_core 0.9.3", "zerocopy 0.8.14", ] @@ -1459,12 +1463,12 @@ dependencies = [ [[package]] name = "rand_chacha" -version = "0.9.0-beta.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16da77124f4ee9fabd55ce6540866e9101431863b4876de58b68797f331adf2" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.0-beta.1", + "rand_core 0.9.3", ] [[package]] @@ -1478,12 +1482,11 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.0-beta.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a98fa0b8309344136abe6244130311e76997e546f76fae8054422a7539b43df7" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.0-rc.0", - "zerocopy 0.8.14", + "getrandom 0.3.2", ] [[package]] @@ -1871,7 +1874,7 @@ dependencies = [ "once_cell", "paste", "prost", - "rand 0.9.0-beta.3", + "rand 0.9.0", "rc4", "rocket", "rustls", @@ -1882,6 +1885,7 @@ dependencies = [ "tokio-stream", "tonic", "tonic-build", + "typenum", "v_byte_macros", ] @@ -2268,9 +2272,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "ubyte" @@ -2348,9 +2352,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] @@ -2624,9 +2628,9 @@ dependencies = [ [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags", ] diff --git a/Cargo.toml b/Cargo.toml index 394af2f..8fab144 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ rocket = { version = "0.5.1", features = ["json", "serde_json"] } serde = { version = "1.0.217", features = ["derive"] } async-trait = "0.1.86" paste = "1.0.15" +typenum = "1.18.0" [build-dependencies] tonic-build = "0.12.3" diff --git a/macros/Cargo.toml b/macros/Cargo.toml index d0bb83b..e301949 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -13,4 +13,5 @@ proc-macro = true quote = "1.0.38" proc-macro2 = "1.0.93" syn = { version = "2.0.98", features = ["full"] } +rand = "0.9.0" diff --git a/macros/src/lib.rs b/macros/src/lib.rs index eb3d5da..5c08c0e 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -323,7 +323,8 @@ pub fn rmc_proto(attr: TokenStream, input: TokenStream) -> TokenStream{ ProtoMethodData{ id, name: func.sig.ident.clone(), - parameters: funcs + parameters: funcs, + ret_val: func.sig.output.clone() } }).collect() diff --git a/macros/src/protos.rs b/macros/src/protos.rs index cc0e5cd..554df22 100644 --- a/macros/src/protos.rs +++ b/macros/src/protos.rs @@ -1,12 +1,13 @@ use proc_macro2::{Ident, Span, TokenStream, TokenTree}; use quote::{quote, ToTokens}; -use syn::{LitInt, Token, Type}; +use syn::{LitInt, ReturnType, Token, Type}; use syn::token::{Brace, Paren, Semi}; pub struct ProtoMethodData{ pub id: LitInt, pub name: Ident, - pub parameters: Vec<(Ident, Type)> + pub parameters: Vec<(Ident, Type)>, + pub ret_val: ReturnType, } @@ -22,8 +23,8 @@ pub struct RmcProtocolData{ pub methods: Vec } -impl ToTokens for RmcProtocolData{ - fn to_tokens(&self, tokens: &mut TokenStream) { +impl RmcProtocolData{ + fn generate_raw_trait(&self, tokens: &mut TokenStream){ let Self{ has_returns, name, @@ -73,7 +74,7 @@ impl ToTokens for RmcProtocolData{ <#param_type as crate::rmc::structures::RmcSerialize>::deserialize( &mut cursor ) else { - return Err(ErrorCode::Core_InvalidArgument); + return Err(crate::rmc::response::ErrorCode::Core_InvalidArgument); }; }.to_tokens(tokens) } @@ -156,6 +157,111 @@ impl ToTokens for RmcProtocolData{ quote!{ impl RawAuth for T{} }.to_tokens(tokens); + } + + fn generate_raw_remote_trait(&self, tokens: &mut TokenStream) { + let Self { + has_returns, + name, + id: proto_id, + methods, + .. + } = self; + + // this gives us the name which the identifier of the corresponding Raw trait + let remote_name = Ident::new(&format!("Remote{}", name), name.span()); + + + // boilerplate tokens which all raw traits need + quote!{ + #[doc(hidden)] + pub trait #remote_name: crate::rmc::protocols::HasRmcConnection + }.to_tokens(tokens); + + // generate the body of the raw protocol trait + Brace::default().surround(tokens, |tokens|{ + //generate each raw method + for method in methods{ + let ProtoMethodData { + name, + parameters, + ret_val, + id: method_id, + .. + } = method; + + quote!{ + async fn #name + }.to_tokens(tokens); + + Paren::default().surround(tokens, |tokens|{ + quote!{ &self, }.to_tokens(tokens); + for (param_ident, param_type) in parameters{ + quote!{ #param_ident: #param_type, }.to_tokens(tokens); + } + }); + + quote!{ + #ret_val + }.to_tokens(tokens); + + Brace::default().surround(tokens, |tokens|{ + quote! { + let mut send_data = Vec::new(); + let mut cursor = ::std::io::Cursor::new(&mut send_data); + }.to_tokens(tokens); + + for (param_name, param_type) in parameters{ + quote!{ + crate::result::ResultExtension::display_err_or_some( + <#param_type as crate::rmc::structures::RmcSerialize>::serialize( + &#param_name, + &mut cursor + ) + ).ok_or(crate::rmc::response::ErrorCode::Core_InvalidArgument)?; + }.to_tokens(tokens) + } + + quote!{ + let call_id = rand::random(); + + let message = crate::rmc::message::RMCMessage{ + call_id, + method_id: #method_id, + protocol_id: #proto_id, + rest_of_data: send_data + }; + + let rmc_conn = ::get_connection(self); + }.to_tokens(tokens); + + if *has_returns{ + quote!{ + crate::result::ResultExtension::display_err_or_some( + rmc_conn.make_raw_call(&message).await + ).ok_or(crate::rmc::response::ErrorCode::Core_Exception) + }.to_tokens(tokens); + } else { + quote!{ + crate::result::ResultExtension::display_err_or_some( + rmc_conn.make_raw_call_no_response(&message).await + ); + }.to_tokens(tokens); + } + + }) + } + }); + + + } + + fn generate_raw_info(&self, tokens: &mut TokenStream){ + let Self{ + name, + id, + .. + } = self; let raw_info_name = Ident::new(&format!("Raw{}Info", name), Span::call_site()); @@ -171,3 +277,15 @@ impl ToTokens for RmcProtocolData{ } } +impl ToTokens for RmcProtocolData{ + fn to_tokens(&self, tokens: &mut TokenStream) { + self.generate_raw_trait(tokens); + self.generate_raw_info(tokens); + self.generate_raw_remote_trait(tokens); + + + + + } +} + diff --git a/src/main.rs b/src/main.rs index cb2d033..b743c62 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,92 +2,115 @@ #![warn(missing_docs)] //! # Splatoon RNEX server -//! -//! This server still includes the code for rnex itself as this is the first rnex server and thus -//! also the first and only current usage of rnex, expect this and rnex to be split into seperate +//! +//! This server still includes the code for rnex itself as this is the first rnex server and thus +//! also the first and only current usage of rnex, expect this and rnex to be split into seperate //! repos soon. -use std::{env, fs}; -use std::fs::File; -use std::net::{Ipv4Addr, SocketAddrV4}; +use crate::rmc::protocols::auth::RemoteAuth; +use crate::rmc::protocols::auth::RawAuthInfo; +use crate::rmc::protocols::auth::RawAuth; +use crate::nex::account::Account; +use crate::prudp::packet::VirtualPort; +use crate::prudp::router::Router; +use crate::prudp::sockaddr::PRUDPSockAddr; +use crate::prudp::socket::Unsecure; use chrono::{Local, SecondsFormat}; use log::info; use once_cell::sync::Lazy; -use simplelog::{ColorChoice, CombinedLogger, Config, LevelFilter, TerminalMode, TermLogger, WriteLogger}; -use crate::nex::account::Account; -use crate::prudp::socket::Unsecure; -use crate::prudp::packet::{VirtualPort}; -use crate::prudp::router::Router; -use crate::prudp::sockaddr::PRUDPSockAddr; +use simplelog::{ + ColorChoice, CombinedLogger, Config, LevelFilter, TermLogger, TerminalMode, WriteLogger, +}; +use std::fs::File; +use std::net::{Ipv4Addr, SocketAddrV4}; +use std::{env, fs}; +use std::marker::PhantomData; +use std::ops::{BitAnd, BitOr}; +use std::str::FromStr; +use macros::rmc_struct; +use crate::rmc::protocols::auth::Auth; +use crate::rmc::protocols::{new_rmc_gateway_connection, OnlyRemote}; +use crate::rmc::response::ErrorCode; +use crate::rmc::structures::any::Any; +use crate::rmc::structures::connection_data::ConnectionData; +use crate::rmc::structures::qresult::QResult; mod endianness; mod prudp; pub mod rmc; //mod protocols; -mod nex; mod grpc; mod kerberos; +mod nex; mod web; +mod versions; +mod result; -static KERBEROS_SERVER_PASSWORD: Lazy = Lazy::new(||{ +static KERBEROS_SERVER_PASSWORD: Lazy = Lazy::new(|| { env::var("AUTH_SERVER_PASSWORD") .ok() .unwrap_or("password".to_owned()) }); +static AUTH_SERVER_ACCOUNT: Lazy = + Lazy::new(|| Account::new(1, "Quazal Authentication", &KERBEROS_SERVER_PASSWORD)); +static SECURE_SERVER_ACCOUNT: Lazy = + Lazy::new(|| Account::new(2, "Quazal Rendez-Vous", &KERBEROS_SERVER_PASSWORD)); -static AUTH_SERVER_ACCOUNT: Lazy = Lazy::new(|| Account::new(1, "Quazal Authentication", &KERBEROS_SERVER_PASSWORD)); -static SECURE_SERVER_ACCOUNT: Lazy = Lazy::new(|| Account::new(2, "Quazal Rendez-Vous", &KERBEROS_SERVER_PASSWORD)); - -static AUTH_SERVER_PORT: Lazy = Lazy::new(||{ +static AUTH_SERVER_PORT: Lazy = Lazy::new(|| { env::var("AUTH_SERVER_PORT") .ok() .and_then(|s| s.parse().ok()) .unwrap_or(10000) }); -static SECURE_SERVER_PORT: Lazy = Lazy::new(||{ +static SECURE_SERVER_PORT: Lazy = Lazy::new(|| { env::var("SECURE_SERVER_PORT") .ok() .and_then(|s| s.parse().ok()) - .unwrap_or(10001) + .unwrap_or(10002) }); -static OWN_IP_PRIVATE: Lazy = Lazy::new(||{ +static OWN_IP_PRIVATE: Lazy = Lazy::new(|| { env::var("SERVER_IP") .ok() .and_then(|s| s.parse().ok()) .expect("no public ip specified") }); -static OWN_IP_PUBLIC: Lazy = Lazy::new(||{ - env::var("SERVER_IP_PUBLIC") - .unwrap_or(OWN_IP_PRIVATE.to_string()) -}); +static OWN_IP_PUBLIC: Lazy = + Lazy::new(|| env::var("SERVER_IP_PUBLIC").unwrap_or(OWN_IP_PRIVATE.to_string())); -static SECURE_STATION_URL: Lazy = Lazy::new(|| - format!("prudps:/PID=2;sid=1;stream=10;type=2;address={};port={};CID=1", *OWN_IP_PUBLIC, *SECURE_SERVER_PORT) -); +static SECURE_STATION_URL: Lazy = Lazy::new(|| { + format!( + "prudps:/PID=2;sid=1;stream=10;type=2;address={};port={};CID=1", + *OWN_IP_PUBLIC, *SECURE_SERVER_PORT + ) +}); #[tokio::main] async fn main() { - CombinedLogger::init( - vec![ - TermLogger::new(LevelFilter::Info, Config::default(), TerminalMode::Mixed, ColorChoice::Auto), - WriteLogger::new(LevelFilter::max(), Config::default(), { - fs::create_dir_all("log").unwrap(); - let date = Local::now().to_rfc3339_opts(SecondsFormat::Secs, false); - // this fixes windows being windows - let date = date.replace(":", "-"); - let filename = format!("{}.log", date); - if cfg!(windows) { - File::create(format!("log\\{}", filename)).unwrap() - } else { - File::create(format!("log/{}", filename)).unwrap() - } - }) - ] - ).unwrap(); + CombinedLogger::init(vec![ + TermLogger::new( + LevelFilter::Info, + Config::default(), + TerminalMode::Mixed, + ColorChoice::Auto, + ), + WriteLogger::new(LevelFilter::max(), Config::default(), { + fs::create_dir_all("log").unwrap(); + let date = Local::now().to_rfc3339_opts(SecondsFormat::Secs, false); + // this fixes windows being windows + let date = date.replace(":", "-"); + let filename = format!("{}.log", date); + if cfg!(windows) { + File::create(format!("log\\{}", filename)).unwrap() + } else { + File::create(format!("log/{}", filename)).unwrap() + } + }), + ]) + .unwrap(); dotenv::dotenv().ok(); @@ -234,68 +257,63 @@ async fn start_secure_server() -> SecureServer{ socket, } }*/ -/* + define_rmc_proto!( proto AuthClientProtocol{ Auth } -);*/ +); +impl Auth for AuthClient{ + async fn login(&self, name: String) -> Result<(), ErrorCode> { + todo!() + } -//#[rmc_struct(AuthClientProtocol)] -struct AuthClient{ + async fn login_ex(&self, name: String, extra_data: Any) -> Result<(QResult, u32, Vec, ConnectionData, String), ErrorCode> { + todo!() + } + async fn request_ticket(&self, source_pid: u32, destination_pid: u32) -> Result<(QResult, Vec), ErrorCode> { + todo!() + } + + async fn get_pid(&self, username: String) -> Result { + todo!() + } + + async fn get_name(&self, pid: u32) -> Result { + todo!() + } } -async fn start_servers(){ +#[rmc_struct(AuthClientProtocol)] +struct AuthClient {} + +async fn start_servers() { + + + //let auth_ip = SocketAddrV4::from_str("157.90.13.221:30039").unwrap(); + let auth_ip = SocketAddrV4::from_str("31.220.75.208:10000").unwrap(); + let auth_port = VirtualPort::new(1, 10); + + let auth_sockaddr = PRUDPSockAddr::new(auth_ip, auth_port); + + let (router_secure, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *SECURE_SERVER_PORT)) + .await + .expect("unable to start router"); + + let mut socket_secure = router_secure + .add_socket(VirtualPort::new(1, 10), Unsecure("CD&ML")) + .await + .expect("unable to add socket"); + + let conn = socket_secure.connect(auth_sockaddr).await.unwrap(); + + let obj = new_rmc_gateway_connection(conn, OnlyRemote::::new); - - - let a = tokio::spawn(async{ - let (router_auth, _) = - Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *AUTH_SERVER_PORT)).await.expect("unable to start router"); - - let mut socket_auth = router_auth.add_socket(VirtualPort::new(1,10), Unsecure("CD&ML")).await - .expect("unable to add socket"); - - let mut conn = socket_auth.accept().await.unwrap(); - info!("got conn"); - - if let Some(data) = conn.recv().await{ - let str = String::from_utf8(data).unwrap(); - - println!("{}", str) - } - }); - - let b = tokio::spawn(async{ - let auth_ip = SocketAddrV4::new(*OWN_IP_PRIVATE, *AUTH_SERVER_PORT); - let auth_port = VirtualPort::new(1,10); - - let auth_sockaddr = PRUDPSockAddr::new(auth_ip,auth_port); - - - let (router_secure, _) = - Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *SECURE_SERVER_PORT)).await.expect("unable to start router"); - - let mut socket_secure = router_secure.add_socket(VirtualPort::new(1,10), Unsecure("CD&ML")).await - .expect("unable to add socket"); - - let conn = socket_secure.connect(auth_sockaddr).await.unwrap(); - - let conn = conn.duplicate_sender(); - - conn.send("Yippie".as_bytes().to_owned()).await; - - info!("got conn"); - }); - - a.await; - b.await; - -/* + /* #[cfg(feature = "auth")] let auth_server = start_auth_server().await; #[cfg(feature = "secure")] @@ -307,4 +325,4 @@ async fn start_servers(){ #[cfg(feature = "secure")] secure_server.join_handle.await.expect("auth server crashed"); web_server.await.expect("webserver crashed");*/ -} +} \ No newline at end of file diff --git a/src/result.rs b/src/result.rs new file mode 100644 index 0000000..31a37d1 --- /dev/null +++ b/src/result.rs @@ -0,0 +1,23 @@ +use std::error::Error; +use log::error; + +pub trait ResultExtension{ + type Output; + + fn display_err_or_some(self) -> Option; +} + +impl ResultExtension for Result{ + type Output = T; + + fn display_err_or_some(self) -> Option { + match self{ + Ok(v) => Some(v), + Err(e) => { + error!("{}", e); + + None + } + } + } +} \ No newline at end of file diff --git a/src/rmc/protocols/auth.rs b/src/rmc/protocols/auth.rs index 5aa84f3..ed8d1cd 100644 --- a/src/rmc/protocols/auth.rs +++ b/src/rmc/protocols/auth.rs @@ -9,9 +9,13 @@ use macros::{method_id, rmc_proto}; /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) #[rmc_proto(10)] pub trait Auth { + /// representation of the `Login` method(for details see the + /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) #[method_id(1)] async fn login(&self, name: String) -> Result<(), ErrorCode>; + /// representation of the `LoginEx` method(for details see the + /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) #[method_id(2)] async fn login_ex( &self, @@ -19,6 +23,8 @@ pub trait Auth { extra_data: Any, ) -> Result<(QResult, u32, Vec, ConnectionData, String), ErrorCode>; + /// representation of the `RequestTicket` method(for details see the + /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) #[method_id(3)] async fn request_ticket( &self, @@ -26,9 +32,16 @@ pub trait Auth { destination_pid: u32, ) -> Result<(QResult, Vec), ErrorCode>; + /// representation of the `GetPID` method(for details see the + /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) #[method_id(4)] async fn get_pid(&self, username: String) -> Result; + /// representation of the `LoginWithContext` method(for details see the + /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) #[method_id(5)] async fn get_name(&self, pid: u32) -> Result; + + // `LoginWithContext` is left out here because we don't need it right now and versioning still + // needs to be figured out } diff --git a/src/rmc/protocols/mod.rs b/src/rmc/protocols/mod.rs index 74c020c..456b402 100644 --- a/src/rmc/protocols/mod.rs +++ b/src/rmc/protocols/mod.rs @@ -2,52 +2,113 @@ pub mod auth; -use macros::method_id; -use std::collections::HashMap; -use std::ops::Add; -use std::sync::{Arc, Condvar}; -use std::time::Duration; +use crate::prudp::socket::{ExternalConnection, SendingConnection}; +use crate::rmc::message::RMCMessage; +use crate::rmc::protocols::RemoteCallError::ConnectionBroke; +use crate::rmc::response::{ErrorCode, RMCResponse, RMCResponseResult}; +use crate::rmc::structures; +use crate::rmc::structures::connection_data::ConnectionData; +use crate::rmc::structures::matchmake::AutoMatchmakeParam; +use crate::rmc::structures::{Error, RmcSerialize}; use async_trait::async_trait; use chrono::TimeDelta; +use log::{error, info}; +use macros::method_id; use macros::{rmc_proto, rmc_struct}; use paste::paste; +use std::collections::HashMap; +use std::io::Cursor; +use std::ops::{Add, Deref}; +use std::sync::{Arc, Condvar}; +use std::time::Duration; +use thiserror::Error; use tokio::sync::{Mutex, Notify}; use tokio::time::{sleep_until, Instant}; -use crate::prudp::socket::{ExternalConnection, SendingConnection}; -use crate::rmc::response::ErrorCode; -use crate::rmc::structures::connection_data::ConnectionData; -use crate::rmc::structures::Error; -use crate::rmc::structures::matchmake::AutoMatchmakeParam; +use crate::result::ResultExtension; + +#[derive(Error, Debug)] +pub enum RemoteCallError { + #[error("Call to remote timed out whilest waiting on response.")] + Timeout, + #[error("A server side rmc error occurred: {0:?}")] + ServerError(ErrorCode), + #[error("Connection broke")] + ConnectionBroke, + #[error("Error reading response data")] + InvalidResponse(#[from] structures::Error), +} pub struct RmcConnection(pub SendingConnection, pub RmcResponseReceiver); -pub struct RmcResponseReceiver(Notify, Mutex>>); +pub struct RmcResponseReceiver(Arc, Arc>>); -impl RmcResponseReceiver{ +impl RmcConnection { + pub async fn make_raw_call( + &self, + message: &RMCMessage, + ) -> Result { + self.make_raw_call_no_response(message).await?; + + let data = self.1.get_response_data(message.call_id).await?; + + let out = ::deserialize(&mut Cursor::new(data))?; + + Ok(out) + } + + pub async fn make_raw_call_no_response( + &self, + message: &RMCMessage, + ) -> Result<(), RemoteCallError> { + let message_data = message.to_data(); + + self.0.send(message_data).await.ok_or(ConnectionBroke)?; + + Ok(()) + } +} + +impl RmcResponseReceiver { // returns none if timed out - pub async fn get_response_data(&self, call_id: u32) -> Option>{ + pub async fn get_response_data(&self, call_id: u32) -> Result, RemoteCallError> { let mut end_wait_time = Instant::now(); end_wait_time += Duration::from_secs(5); let sleep_fut = sleep_until(end_wait_time); tokio::pin!(sleep_fut); + let mut sleep_manual_unlock_fut = Instant::now(); + sleep_manual_unlock_fut += Duration::from_secs(4); + + let sleep_manual_unlock_fut = sleep_until(sleep_manual_unlock_fut); + tokio::pin!(sleep_manual_unlock_fut); + loop { let mut locked = self.1.lock().await; - if let Some(v) = locked.remove(&call_id){ - return Some(v); + if let Some(v) = locked.remove(&call_id) { + match v.response_result{ + RMCResponseResult::Success { + data, + .. + } => return Ok(data), + RMCResponseResult::Error { + error_code, + .. + } => return Err(RemoteCallError::ServerError(error_code)) + } } drop(locked); - let notif_fut = self.0.notified(); - tokio::select! { + _ = &mut sleep_manual_unlock_fut => { + continue; + } _ = &mut sleep_fut => { - return None; + return Err(RemoteCallError::Timeout); } _ = notif_fut => { continue; @@ -57,28 +118,30 @@ impl RmcResponseReceiver{ } } -pub trait HasRmcConnection{ - fn get_response_receiver(&self) -> &RmcConnection; +pub trait HasRmcConnection { + fn get_connection(&self) -> &RmcConnection; } -pub trait RemoteObject{ +pub trait RemoteObject { fn new(conn: RmcConnection) -> Self; } -impl RemoteObject for (){ +impl RemoteObject for () { fn new(_: RmcConnection) -> Self {} } - - -pub trait RmcCallable{ +pub trait RmcCallable { //type Remote: RemoteObject; - //fn new_callable(remote: Self::Remote); - async fn rmc_call(&self, responder: &SendingConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec); + async fn rmc_call( + &self, + responder: &SendingConnection, + protocol_id: u16, + method_id: u32, + call_id: u32, + rest: Vec, + ); } - - #[macro_export] macro_rules! define_rmc_proto { (proto $name:ident{ @@ -98,7 +161,131 @@ macro_rules! define_rmc_proto { struct [](crate::rmc::protocols::RmcConnection); + impl crate::rmc::protocols::RemoteInstantiatable for []{ + fn new(conn: crate::rmc::protocols::RmcConnection) -> Self{ + Self(conn) + } + } + impl crate::rmc::protocols::HasRmcConnection for []{ + fn get_connection(&self) -> &crate::rmc::protocols::RmcConnection{ + &self.0 + } + } + + $( + impl [] for []{} + )* } }; -} \ No newline at end of file +} + +/// This is a special case to allow unit to represent the fact that no object is represented. +impl RmcCallable for () { + async fn rmc_call( + &self, + remote_response_connection: &crate::prudp::socket::SendingConnection, + protocol_id: u16, + method_id: u32, + call_id: u32, + rest: Vec, + ) { + //todo: maybe reply with not implemented(?) + } +} + +pub trait RemoteInstantiatable{ + fn new(conn: RmcConnection) -> Self; +} + +pub struct OnlyRemote(T); + +impl Deref for OnlyRemote{ + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl OnlyRemote{ + pub fn new(conn: RmcConnection) -> Self{ + Self(T::new(conn)) + } +} + +impl RmcCallable for OnlyRemote{ + async fn rmc_call(&self, responder: &SendingConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec) { + + } +} + +async fn handle_incoming( + mut connection: ExternalConnection, + remote: Arc, + notify: Arc, + incoming: Arc>>, +) { + let sending_conn = connection.duplicate_sender(); + + while let Some(v) = connection.recv().await{ + let Some(proto_id) = v.get(5) else { + error!("received too small rmc message."); + error!("ending rmc gateway."); + return + }; + + if proto_id & 0x80 == 0{ + let Some(response) = RMCResponse::new(&mut Cursor::new(v)).display_err_or_some() else { + error!("ending rmc gateway."); + return + }; + + info!("got rmc response"); + + let mut locked = incoming.lock().await; + + locked.insert(response.get_call_id(), response); + notify.notify_waiters(); + } else { + let Some(message) = RMCMessage::new(&mut Cursor::new(v)).display_err_or_some() else { + error!("ending rmc gateway."); + return + }; + + info!("got rmc request"); + } + } +} + +pub fn new_rmc_gateway_connection(conn: ExternalConnection, create_internal: F) -> Arc +where + F: FnOnce(RmcConnection) -> T, +{ + let notify = Arc::new(Notify::new()); + let incoming: Arc>> = Default::default(); + + let response_recv = RmcResponseReceiver(notify.clone(), incoming.clone()); + + let sending_conn = conn.duplicate_sender(); + + let rmc_conn = RmcConnection(sending_conn, response_recv); + + let exposed_object = (create_internal)(rmc_conn); + + let exposed_object = Arc::new(exposed_object); + + { + let exposed_object = exposed_object.clone(); + tokio::spawn(async move { + handle_incoming( + conn, + exposed_object, + notify, + incoming + ).await; + }); + } + + exposed_object +} diff --git a/src/rmc/response.rs b/src/rmc/response.rs index ece1486..84bd529 100644 --- a/src/rmc/response.rs +++ b/src/rmc/response.rs @@ -1,12 +1,16 @@ use std::io; -use std::io::{Write}; +use std::io::{Read, Seek, Write}; use std::mem::transmute; use bytemuck::bytes_of; +use log::error; +use v_byte_macros::EnumTryInto; +use crate::endianness::{ReadExtensions, IS_BIG_ENDIAN}; use crate::prudp::packet::{PRUDPPacket}; use crate::prudp::packet::flags::{NEED_ACK, RELIABLE}; use crate::prudp::packet::PacketOption::FragmentId; use crate::prudp::packet::types::DATA; use crate::prudp::socket::{ExternalConnection, SendingConnection}; +use crate::rmc::response::ErrorCode::Core_Exception; use crate::rmc::structures::qresult::ERROR_MASK; use crate::rmc::structures::RmcSerialize; use crate::web::DirectionalData::{Incoming, Outgoing}; @@ -30,6 +34,69 @@ pub struct RMCResponse { } impl RMCResponse { + pub fn new(stream: &mut (impl Seek + Read)) -> io::Result{ + // ignore the size for now this will only be used for checking + let _: u32 = stream.read_struct(IS_BIG_ENDIAN)?; + + let protocol_id: u8 = stream.read_struct(IS_BIG_ENDIAN)?; + + /*let protocol_id: u16 = match protocol_id{ + 0x7F => { + stream.read_struct(IS_BIG_ENDIAN)? + }, + _ => protocol_id as u16 + };*/ + + let is_success: u8 = stream.read_struct(IS_BIG_ENDIAN)?; + + let response_result = if is_success == 0x01{ + let call_id: u32 = stream.read_struct(IS_BIG_ENDIAN)?; + let method_id: u32 = stream.read_struct(IS_BIG_ENDIAN)?; + let method_id = method_id & (!0x8000); + + let mut data: Vec = Vec::new(); + + stream.read_to_end(&mut data)?; + + + RMCResponseResult::Success { + call_id, + method_id, + data + } + } else { + let error_code: u32 = stream.read_struct(IS_BIG_ENDIAN)?; + let error_code = error_code & (!0x80000000); + let call_id: u32 = stream.read_struct(IS_BIG_ENDIAN)?; + + RMCResponseResult::Error { + error_code: { + match ErrorCode::try_from(error_code){ + Ok(v) => v, + Err(e) => { + error!("invalid error code {:#010x}", error_code); + Core_Exception + } + } + }, + call_id, + + } + }; + + Ok(Self{ + protocol_id, + response_result + }) + } + + pub fn get_call_id(&self) -> u32{ + match &self.response_result{ + RMCResponseResult::Success { call_id, ..} => *call_id, + RMCResponseResult::Error { call_id, .. } => *call_id + } + } + pub fn to_data(self) -> Vec { generate_response(self.protocol_id, self.response_result).expect("failed to generate response") } @@ -120,6 +187,7 @@ pub async fn send_response(connection: &SendingConnection, rmcresponse: RMCRespo //taken from kinnays error list directly #[allow(nonstandard_style)] #[repr(u32)] +#[derive(Debug, EnumTryInto)] pub enum ErrorCode { Core_Unknown = 0x00010001, Core_NotImplemented = 0x00010002, diff --git a/src/rmc/structures/any.rs b/src/rmc/structures/any.rs index cc1de19..d402f3e 100644 --- a/src/rmc/structures/any.rs +++ b/src/rmc/structures/any.rs @@ -2,15 +2,24 @@ use std::io::{Read, Write}; use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; use super::{Result, RmcSerialize}; -#[derive(Debug)] +#[derive(Debug, Default)] pub struct Any{ pub name: String, pub data: Vec } impl RmcSerialize for Any{ - fn serialize(&self, _writer: &mut dyn Write) -> Result<()> { - todo!() + fn serialize(&self, writer: &mut dyn Write) -> Result<()> { + self.name.serialize(writer)?; + + let u32_len = self.data.len() as u32; + + u32_len.serialize(writer)?; + u32_len.serialize(writer)?; + + self.data.serialize(writer)?; + + Ok(()) } fn deserialize(mut reader: &mut dyn Read) -> Result { let name = String::deserialize(reader)?; diff --git a/src/rmc/structures/connection_data.rs b/src/rmc/structures/connection_data.rs index de20491..d0c27b1 100644 --- a/src/rmc/structures/connection_data.rs +++ b/src/rmc/structures/connection_data.rs @@ -3,6 +3,7 @@ use bytemuck::bytes_of; use crate::kerberos::KerberosDateTime; use crate::rmc::structures::{rmc_struct, RmcSerialize}; +#[derive(Debug)] pub struct ConnectionData<'a>{ pub station_url: &'a str, pub special_protocols: Vec, diff --git a/src/rmc/structures/qresult.rs b/src/rmc/structures/qresult.rs index d4a5078..41a5263 100644 --- a/src/rmc/structures/qresult.rs +++ b/src/rmc/structures/qresult.rs @@ -7,7 +7,7 @@ use crate::rmc::structures::{RmcSerialize, Result}; pub const ERROR_MASK: u32 = 1 << 31; -#[derive(Pod, Zeroable, Copy, Clone, SwapEndian)] +#[derive(Pod, Zeroable, Copy, Clone, SwapEndian, Debug)] #[repr(transparent)] pub struct QResult(u32); diff --git a/src/versions.rs b/src/versions.rs new file mode 100644 index 0000000..0fa85fb --- /dev/null +++ b/src/versions.rs @@ -0,0 +1,97 @@ +use std::marker::PhantomData; +use std::ops::{BitAnd, BitOr}; +use typenum::{Cmp, IsEqual, IsLess, IsLessOrEqual, Unsigned, U1, U2, U3}; + +/// This trait represents a version at compile time +trait Version{ + type Major: Unsigned; + type Minor: Unsigned; +} + +/// This struct contains nothing and is used to represent specific versions as an instance of +/// [`Version`]. It is instances as `Ver` +struct Ver{ + _phantom: PhantomData<(MAJ, MIN)> +} + +impl Version for Ver{ + type Major = MAJ; + type Minor = MIN; +} + +/// Represents two versions which can be compared +trait ComparableVersion: Version{ + type IsAtLeast: SameOrUnit; +} + +impl ComparableVersion for U where + ::Major: Cmp, + ::Minor: IsLessOrEqual, + ::Major: IsEqual< + Self::Major, + Output: BitAnd< + typenum::LeEq + >, + >, + ::Major: IsLess< + Self::Major, + Output: BitOr< + typenum::And< + typenum::Eq, + typenum::LeEq, + >, + Output: SameOrUnit + > + > { + + type IsAtLeast = typenum::Or< + typenum::Le, + typenum::And< + typenum::Eq, + typenum::LeEq, + > + >; +} + + +/// Simple check for testing if the `TEST` version is at least `REQ` or higher. +type VersionAbove = >::IsAtLeast; + +trait VersionIsAtLeast{} + +impl> VersionIsAtLeast for T{} + + +/// Trait for containing the result of elements which only conditionally exist +trait CondElemResult{ + type Output; +} + +/// Empty helper struct which only servers to give a concrete type when creating fields in rmc +/// structs which have a version requirement. This is not meant to be used directly, use +/// [`MinVersion`] instead. +struct MinVersionElementHelper>{ + _phantom: PhantomData<(T, REQUIRED, VER)> +} + +/// This should be used either with [`typenum::True`] or [`typenum::False`]. When `True` the [`Self::Output`] +/// will be the same as the `T` you put into Output. When `False` it will always be `()` +trait SameOrUnit{ + type Output; +} + +impl SameOrUnit for typenum::True{ + type Output = T; +} + +impl SameOrUnit for typenum::False{ + type Output = (); +} + +impl> CondElemResult for MinVersionElementHelper where { + type Output = <>::IsAtLeast as SameOrUnit>::Output; +} + +/// When the version condition is met the field will exist and will simply be `T` if not it will be +/// replaced by `()`. Use this when you need to add versioning to rmc structs. +type MinVersion = as CondElemResult>::Output; \ No newline at end of file From 2450341b5d1a31558c2c1e86fa848aa842abe135 Mon Sep 17 00:00:00 2001 From: andrea <1-ssdrive@users.noreply.git.perditum.com> Date: Tue, 25 Mar 2025 07:30:10 +0000 Subject: [PATCH 08/14] Merge main into gatherings --- .dockerignore | 2 + .gitlab-ci.yml | 19 ++ Cargo.toml | 11 + Dockerfile | 19 ++ LICENSE | 33 +++ LICENSE.txt | 674 ------------------------------------------------- 6 files changed, 84 insertions(+), 674 deletions(-) create mode 100644 .dockerignore create mode 100644 .gitlab-ci.yml create mode 100644 Dockerfile create mode 100644 LICENSE delete mode 100644 LICENSE.txt diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..0d95126 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.env +target \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..56c6940 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,19 @@ +image: docker:latest + +variables: + IMAGE_NAME: "ci.perditum.com/perditum/rnex-splatoon" + IMAGE_TAG: "${CI_COMMIT_REF_SLUG}" + +before_script: + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" ci.perditum.com + +stages: + - build-and-push + +build-and-push: + stage: build-and-push + script: + - docker build -t "$IMAGE_NAME:$IMAGE_TAG" . + - docker tag "$IMAGE_NAME:$IMAGE_TAG" "$IMAGE_NAME:latest" + - docker push "$IMAGE_NAME:$IMAGE_TAG" + - docker push "$IMAGE_NAME:latest" diff --git a/Cargo.toml b/Cargo.toml index 8fab144..edbb65b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,17 @@ name = "splatoon-server-rust" version = "0.1.0" edition = "2021" +[profile.prod] +inherits = "release" +overflow-checks = false +strip = true +debug = false +debug-assertions = false +lto = true +incremental = false + + + [dependencies] bytemuck = { version = "1.21.0", features = ["derive"] } dotenv = "0.15.0" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..39ae51c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM rust:alpine AS builder + +WORKDIR /app + +COPY . . + +RUN apk add --no-cache protobuf-dev git musl-dev lld + +RUN git submodule update --init --recursive + +RUN RUSTFLAGS="-C relocation-model=static -C linker=ld.lld" cargo build --profile prod --target x86_64-unknown-linux-musl + +FROM scratch AS final + +# Copy the compiled binary from the builder stage +COPY --from=builder /app/target/x86_64-unknown-linux-musl/prod/splatoon-server-rust /splatoon-server-rust + +# Command to run the application +ENTRYPOINT ["/splatoon-server-rust"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..683b4a5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,33 @@ + RNEX License + + Version 1.1, 2025 + + Preamble + +The RNEX License is a free software license designed to ensure that all modifications to this software remain open, attribution is maintained, and forks outside of the designated platform require approval. + +Please note that all terms of the GPLv3.0 also apply, in addition to the following terms. + + + + Terms and Conditions + +1. Grant of Use +You are granted permission to use, modify, distribute, and sublicense this software under the following conditions. + +2. Copyleft Requirement +Any modification or derivative work based on this software must be publicly released under this same RNEX License. + +3. Attribution +All copies or substantial portions of the software, modified or unmodified, must retain the original authorship attribution as specified in the source code. + +4. External Forks +Forking and hosting this software on external platforms that are *not* PerditumGit require explicit approval from the project maintainers via an issue request. If approval is not granted, the fork must be removed immediately. + +5. No Additional Restrictions +You may not impose any additional restrictions on the exercise of the rights granted under this License. + +6. Disclaimer of Warranty +This software is provided "as-is" without warranty of any kind, express or implied. The authors are not liable for any damages resulting from its use. + +By using, modifying, or distributing this software, you agree to abide by these terms. diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index f288702..0000000 --- a/LICENSE.txt +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. From c40ca2baf3614a78575cab00fd27a8de6aeda1cb Mon Sep 17 00:00:00 2001 From: Andrea Toska Date: Tue, 25 Mar 2025 08:32:23 +0100 Subject: [PATCH 09/14] Remove GitHub CI we're on gitlab now lol --- .github/workflows/build.yml | 30 ------------------------------ .github/workflows/stale.yml | 14 -------------- 2 files changed, 44 deletions(-) delete mode 100644 .github/workflows/build.yml delete mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index a1f51b3..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Build - -on: - push: - branches: - - main - pull_request: - branches: - - main - -env: - CARGO_TERM_COLOR: always - -jobs: - build: - runs-on: self-hosted - steps: - - name: Checkout Repository And Submodules - uses: actions/checkout@v1 - with: - submodules: recursive - - - name: Setup Rust - run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -q -y - - - name: Build - run: . "$HOME/.cargo/env" && cargo build --verbose - - - name: Run tests - run: . "$HOME/.cargo/env" && cargo test --verbose \ No newline at end of file diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml deleted file mode 100644 index 4618492..0000000 --- a/.github/workflows/stale.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: 'Close stale issues and PRs' -on: - schedule: - - cron: '30 1 * * *' - -jobs: - stale: - runs-on: debian-latest - steps: - - uses: actions/stale@v9 - with: - stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.' - days-before-stale: 10 - days-before-close: 5 From e1a08d6c7ba4569739cd6cae30329238ac72197f Mon Sep 17 00:00:00 2001 From: Tim Nebel Date: Tue, 25 Mar 2025 10:13:19 +0000 Subject: [PATCH 10/14] feat: add handelinng incoming messages --- src/rmc/protocols/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/rmc/protocols/mod.rs b/src/rmc/protocols/mod.rs index 456b402..2dfbbf8 100644 --- a/src/rmc/protocols/mod.rs +++ b/src/rmc/protocols/mod.rs @@ -253,6 +253,15 @@ async fn handle_incoming( return }; + let RMCMessage{ + protocol_id, + method_id, + call_id, + rest_of_data + } = message; + + remote.rmc_call(&sending_conn, protocol_id, method_id, call_id, rest_of_data).await; + info!("got rmc request"); } } From ac215819578dbf96b99bf593cd5e24dfcf7393fd Mon Sep 17 00:00:00 2001 From: Tim Nebel Date: Tue, 25 Mar 2025 12:42:51 +0000 Subject: [PATCH 11/14] fix not compiling --- src/rmc/protocols/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rmc/protocols/mod.rs b/src/rmc/protocols/mod.rs index 2dfbbf8..6234149 100644 --- a/src/rmc/protocols/mod.rs +++ b/src/rmc/protocols/mod.rs @@ -220,7 +220,7 @@ impl RmcCallable for OnlyRemote{ } } -async fn handle_incoming( +async fn handle_incoming( mut connection: ExternalConnection, remote: Arc, notify: Arc, From 803a98aa0bcaf8977df0b8836efeea47fff160a0 Mon Sep 17 00:00:00 2001 From: DJMrTV Date: Tue, 25 Mar 2025 22:53:43 +0100 Subject: [PATCH 12/14] feat: fix prudp stuff(almost there) --- src/main.rs | 39 +++++++++++++++++-------- src/prudp/router.rs | 2 ++ src/prudp/sockaddr.rs | 2 +- src/prudp/socket.rs | 62 ++++++++++++++++++++++++++++++++++------ src/rmc/protocols/mod.rs | 10 +++---- 5 files changed, 88 insertions(+), 27 deletions(-) diff --git a/src/main.rs b/src/main.rs index b743c62..f9190a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ use crate::prudp::router::Router; use crate::prudp::sockaddr::PRUDPSockAddr; use crate::prudp::socket::Unsecure; use chrono::{Local, SecondsFormat}; -use log::info; +use log::{error, info}; use once_cell::sync::Lazy; use simplelog::{ ColorChoice, CombinedLogger, Config, LevelFilter, TermLogger, TerminalMode, WriteLogger, @@ -68,7 +68,7 @@ static SECURE_SERVER_PORT: Lazy = Lazy::new(|| { env::var("SECURE_SERVER_PORT") .ok() .and_then(|s| s.parse().ok()) - .unwrap_or(10002) + .unwrap_or(10001) }); static OWN_IP_PRIVATE: Lazy = Lazy::new(|| { @@ -258,11 +258,6 @@ async fn start_secure_server() -> SecureServer{ } }*/ -define_rmc_proto!( - proto AuthClientProtocol{ - Auth - } -); impl Auth for AuthClient{ async fn login(&self, name: String) -> Result<(), ErrorCode> { @@ -287,7 +282,16 @@ impl Auth for AuthClient{ } #[rmc_struct(AuthClientProtocol)] -struct AuthClient {} +struct AuthClient { + +} + +define_rmc_proto!( + proto AuthClientProtocol{ + Auth + } +); + async fn start_servers() { @@ -298,18 +302,29 @@ async fn start_servers() { let auth_sockaddr = PRUDPSockAddr::new(auth_ip, auth_port); - let (router_secure, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *SECURE_SERVER_PORT)) + let (router_secure, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *AUTH_SERVER_PORT)) .await .expect("unable to start router"); let mut socket_secure = router_secure - .add_socket(VirtualPort::new(1, 10), Unsecure("CD&ML")) + .add_socket(VirtualPort::new(1, 10), Unsecure("6f599f81")) .await .expect("unable to add socket"); - let conn = socket_secure.connect(auth_sockaddr).await.unwrap(); + // let conn = socket_secure.connect(auth_sockaddr).await.unwrap(); - let obj = new_rmc_gateway_connection(conn, OnlyRemote::::new); + + loop { + let Some(conn) = socket_secure.accept().await else { + error!("server crashed"); + return; + }; + + info!("new connected user!"); + + let _ = new_rmc_gateway_connection(conn, |_| AuthClient {}); //OnlyRemote::::new + + } diff --git a/src/prudp/router.rs b/src/prudp/router.rs index 5fd4198..16424c6 100644 --- a/src/prudp/router.rs +++ b/src/prudp/router.rs @@ -51,6 +51,8 @@ impl Router { let connection = packet.source_sockaddr(addr); + println!("data from {:?}", connection); + let endpoints = self.endpoints.read().await; let Some(endpoint) = endpoints[packet.header.destination_port.get_port_number() as usize].as_ref() else { diff --git a/src/prudp/sockaddr.rs b/src/prudp/sockaddr.rs index 75254bf..fe6a9d8 100644 --- a/src/prudp/sockaddr.rs +++ b/src/prudp/sockaddr.rs @@ -26,7 +26,7 @@ impl PRUDPSockAddr{ let mut hmac = Md5Hmac::new_from_slice(&[0; 16]).expect("fuck"); let mut data = self.regular_socket_addr.ip().octets().to_vec(); - data.extend_from_slice(&self.regular_socket_addr.port().to_be_bytes()); + //data.extend_from_slice(&self.regular_socket_addr.port().to_be_bytes()); hmac.write_all(&data).expect("figuring this out was complete ass"); let result: [u8; 16] = hmac.finalize().into_bytes()[0..16].try_into().expect("fuck"); diff --git a/src/prudp/socket.rs b/src/prudp/socket.rs index a0429c8..6b5c6a2 100644 --- a/src/prudp/socket.rs +++ b/src/prudp/socket.rs @@ -1,6 +1,6 @@ use crate::prudp::packet::flags::{ACK, HAS_SIZE, MULTI_ACK, NEED_ACK, RELIABLE}; use crate::prudp::packet::types::{CONNECT, DATA, DISCONNECT, PING, SYN}; -use crate::prudp::packet::PacketOption::{ConnectionSignature, FragmentId, MaximumSubstreamId, SupportedFunctions}; +use crate::prudp::packet::PacketOption::{ConnectionSignature, FragmentId, InitialSequenceId, MaximumSubstreamId, SupportedFunctions}; use crate::prudp::packet::{PRUDPHeader, PRUDPPacket, PacketOption, TypesFlags, VirtualPort}; use crate::prudp::router::{Error, Router}; use crate::prudp::sockaddr::PRUDPSockAddr; @@ -670,7 +670,7 @@ impl InternalSocket { .write_to(&mut vec) .expect("somehow failed to convert backet to bytes"); - println!("{}", hex::encode(&vec)); + println!("sent out: {}", hex::encode(&vec)); self.socket .send_to(&vec, dest.regular_socket_addr) @@ -681,7 +681,11 @@ impl InternalSocket { async fn handle_syn(&self, address: PRUDPSockAddr, packet: PRUDPPacket) { info!("got syn"); - let mut response = packet.base_acknowledgement_packet(); + let mut response = packet.base_response_packet(); + + response.header.types_and_flags.set_types(SYN); + response.header.types_and_flags.set_flag(ACK); + response.header.types_and_flags.set_flag(HAS_SIZE); let signature = address.calculate_connection_signature(); @@ -700,10 +704,12 @@ impl InternalSocket { } } - response.header.types_and_flags.set_flag(HAS_SIZE | ACK); + response.set_sizes(); self.crypto_handler.sign_pre_handshake(&mut response); + //println!("got syn: {:?}", response); + self.send_packet_unbuffered(address, response) .await; } @@ -822,16 +828,45 @@ impl InternalSocket { remote_signature, *own_signature, &packet.payload, - *max_substream, + 1 + *max_substream, ); - let mut response = packet.base_acknowledgement_packet(); - response.header.types_and_flags.set_flag(HAS_SIZE | ACK); + let mut response = packet.base_response_packet(); + response.header.types_and_flags.set_types(CONNECT); + response.header.types_and_flags.set_flag(ACK); + response.header.types_and_flags.set_flag(HAS_SIZE); + response.header.session_id = session_id; + response.header.sequence_id = 1; + response.payload = return_data; + + //let remote_signature = address.calculate_connection_signature(); + + response + .options + .push(ConnectionSignature(Default::default())); + + for option in &packet.options { + match option { + MaximumSubstreamId(max_substream) => response + .options + .push(MaximumSubstreamId(*max_substream)), + SupportedFunctions(funcs) => { + response.options.push(SupportedFunctions(*funcs)) + } + _ => { /* ? */ } + } + } + + + response.set_sizes(); + crypto.sign_connect(&mut response); + //println!("connect out: {:?}", response); + self.create_connection(crypto, address, session_id).await; self.send_packet_unbuffered(address, response).await; @@ -926,6 +961,15 @@ impl AnyInternalSocket for InternalSocket { info!("got ack"); if packet.header.types_and_flags.get_types() == SYN || packet.header.types_and_flags.get_types() == CONNECT{ + + if packet.header.types_and_flags.get_types() == SYN{ + println!("Syn: {:?}", packet); + } + + if packet.header.types_and_flags.get_types() == CONNECT{ + println!("Connect: {:?}", packet); + } + let sender = self.connection_establishment_data_sender.lock().await; info!("redirecting ack to active connection establishment code"); @@ -976,7 +1020,7 @@ impl AnyInternalSocket for InternalSocket { }, options: vec![ SupportedFunctions(0x104), - MaximumSubstreamId(1), + MaximumSubstreamId(0), ConnectionSignature(remote_signature) ], ..Default::default() @@ -1011,7 +1055,7 @@ impl AnyInternalSocket for InternalSocket { }, options: vec![ SupportedFunctions(0x04), - MaximumSubstreamId(1), + MaximumSubstreamId(0), ConnectionSignature(remote_signature) ], ..Default::default() diff --git a/src/rmc/protocols/mod.rs b/src/rmc/protocols/mod.rs index 6234149..38430df 100644 --- a/src/rmc/protocols/mod.rs +++ b/src/rmc/protocols/mod.rs @@ -132,14 +132,14 @@ impl RemoteObject for () { pub trait RmcCallable { //type Remote: RemoteObject; - async fn rmc_call( + fn rmc_call( &self, responder: &SendingConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec, - ); + ) -> impl std::future::Future + Send; } #[macro_export] @@ -215,8 +215,8 @@ impl OnlyRemote{ } impl RmcCallable for OnlyRemote{ - async fn rmc_call(&self, responder: &SendingConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec) { - + fn rmc_call(&self, responder: &SendingConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec) -> impl std::future::Future + Send { + async{} } } @@ -229,7 +229,7 @@ async fn handle_incoming( let sending_conn = connection.duplicate_sender(); while let Some(v) = connection.recv().await{ - let Some(proto_id) = v.get(5) else { + let Some(proto_id) = v.get(4) else { error!("received too small rmc message."); error!("ending rmc gateway."); return From 5e726fc9b0d33f34bdd73fd93d8e22f06f276948 Mon Sep 17 00:00:00 2001 From: DJMrTV Date: Wed, 7 May 2025 21:52:05 +0200 Subject: [PATCH 13/14] feat: rnex works, all major roadblocks are gone, the rnex rewrite is now in working state --- src/main.rs | 178 ++++---- src/nex/auth_handler.rs | 167 ++++++++ src/nex/mod.rs | 3 +- src/prudp/mod.rs | 5 +- src/prudp/router.rs | 3 +- src/prudp/secure.rs | 94 ++++- src/prudp/socket.rs | 576 +------------------------- src/prudp/unsecure.rs | 84 ++++ src/rmc/protocols/mod.rs | 12 +- src/rmc/response.rs | 16 +- src/rmc/structures/connection_data.rs | 27 +- src/rmc/structures/rmc_struct.rs | 2 +- 12 files changed, 497 insertions(+), 670 deletions(-) create mode 100644 src/nex/auth_handler.rs create mode 100644 src/prudp/unsecure.rs diff --git a/src/main.rs b/src/main.rs index f9190a6..8f49e9b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,33 +7,37 @@ //! also the first and only current usage of rnex, expect this and rnex to be split into seperate //! repos soon. -use crate::rmc::protocols::auth::RemoteAuth; -use crate::rmc::protocols::auth::RawAuthInfo; -use crate::rmc::protocols::auth::RawAuth; use crate::nex::account::Account; +use crate::nex::auth_handler::{AuthHandler, RemoteAuthClientProtocol}; use crate::prudp::packet::VirtualPort; use crate::prudp::router::Router; +use crate::prudp::secure::Secure; use crate::prudp::sockaddr::PRUDPSockAddr; -use crate::prudp::socket::Unsecure; -use chrono::{Local, SecondsFormat}; -use log::{error, info}; -use once_cell::sync::Lazy; -use simplelog::{ - ColorChoice, CombinedLogger, Config, LevelFilter, TermLogger, TerminalMode, WriteLogger, -}; -use std::fs::File; -use std::net::{Ipv4Addr, SocketAddrV4}; -use std::{env, fs}; -use std::marker::PhantomData; -use std::ops::{BitAnd, BitOr}; -use std::str::FromStr; -use macros::rmc_struct; +use crate::prudp::unsecure::Unsecure; use crate::rmc::protocols::auth::Auth; +use crate::rmc::protocols::auth::RawAuth; +use crate::rmc::protocols::auth::RawAuthInfo; +use crate::rmc::protocols::auth::RemoteAuth; use crate::rmc::protocols::{new_rmc_gateway_connection, OnlyRemote}; use crate::rmc::response::ErrorCode; use crate::rmc::structures::any::Any; use crate::rmc::structures::connection_data::ConnectionData; use crate::rmc::structures::qresult::QResult; +use chrono::{Local, SecondsFormat}; +use log::{error, info}; +use macros::rmc_struct; +use once_cell::sync::Lazy; +use simplelog::{ + ColorChoice, CombinedLogger, Config, LevelFilter, TermLogger, TerminalMode, WriteLogger, +}; +use std::fs::File; +use std::marker::PhantomData; +use std::net::{Ipv4Addr, SocketAddrV4}; +use std::ops::{BitAnd, BitOr}; +use std::str::FromStr; +use std::time::Duration; +use std::{env, fs}; +use tokio::task::JoinHandle; mod endianness; mod prudp; @@ -43,9 +47,9 @@ pub mod rmc; mod grpc; mod kerberos; mod nex; -mod web; -mod versions; mod result; +mod versions; +mod web; static KERBEROS_SERVER_PASSWORD: Lazy = Lazy::new(|| { env::var("AUTH_SERVER_PASSWORD") @@ -258,86 +262,112 @@ async fn start_secure_server() -> SecureServer{ } }*/ +async fn start_auth() -> JoinHandle<()> { + tokio::spawn(async { + let (router_secure, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *AUTH_SERVER_PORT)) + .await + .expect("unable to start router"); -impl Auth for AuthClient{ - async fn login(&self, name: String) -> Result<(), ErrorCode> { - todo!() - } + let mut socket_secure = router_secure + .add_socket(VirtualPort::new(1, 10), Unsecure("6f599f81")) + .await + .expect("unable to add socket"); - async fn login_ex(&self, name: String, extra_data: Any) -> Result<(QResult, u32, Vec, ConnectionData, String), ErrorCode> { - todo!() - } + // let conn = socket_secure.connect(auth_sockaddr).await.unwrap(); - async fn request_ticket(&self, source_pid: u32, destination_pid: u32) -> Result<(QResult, Vec), ErrorCode> { - todo!() - } + loop { + let Some(conn) = socket_secure.accept().await else { + error!("server crashed"); + return; + }; - async fn get_pid(&self, username: String) -> Result { - todo!() - } + info!("new connected user!"); - async fn get_name(&self, pid: u32) -> Result { - todo!() - } + let _ = new_rmc_gateway_connection(conn, |_| AuthHandler { + destination_server_acct: &SECURE_SERVER_ACCOUNT, + build_name: "branch:origin/project/wup-agmj build:3_8_15_2004_0", + station_url: &SECURE_STATION_URL, + }); + } + }) } -#[rmc_struct(AuthClientProtocol)] -struct AuthClient { +async fn start_secure() -> JoinHandle<()> { + tokio::spawn(async { + let (router_secure, _) = + Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *SECURE_SERVER_PORT)) + .await + .expect("unable to start router"); + let mut socket_secure = router_secure + .add_socket( + VirtualPort::new(1, 10), + Secure("6f599f81", &SECURE_SERVER_ACCOUNT), + ) + .await + .expect("unable to add socket"); + + // let conn = socket_secure.connect(auth_sockaddr).await.unwrap(); + + loop { + let Some(conn) = socket_secure.accept().await else { + error!("server crashed"); + return; + }; + + info!("new connected user on secure :D!"); + + let _ = new_rmc_gateway_connection(conn, |_| AuthHandler { + destination_server_acct: &SECURE_SERVER_ACCOUNT, + build_name: "branch:origin/project/wup-agmj build:3_8_15_2004_0", + station_url: &SECURE_STATION_URL, + }); + } + }) } -define_rmc_proto!( - proto AuthClientProtocol{ - Auth - } -); +async fn start_test() { + let addr = SocketAddrV4::new(*OWN_IP_PRIVATE, *AUTH_SERVER_PORT); + let virt_addr = VirtualPort::new(1, 10); + let prudp_addr = PRUDPSockAddr::new(addr, virt_addr); -async fn start_servers() { - - - //let auth_ip = SocketAddrV4::from_str("157.90.13.221:30039").unwrap(); - let auth_ip = SocketAddrV4::from_str("31.220.75.208:10000").unwrap(); - let auth_port = VirtualPort::new(1, 10); - - let auth_sockaddr = PRUDPSockAddr::new(auth_ip, auth_port); - - let (router_secure, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *AUTH_SERVER_PORT)) + let (router_test, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, 26969)) .await .expect("unable to start router"); - let mut socket_secure = router_secure + let mut socket_secure = router_test .add_socket(VirtualPort::new(1, 10), Unsecure("6f599f81")) .await .expect("unable to add socket"); - // let conn = socket_secure.connect(auth_sockaddr).await.unwrap(); + let conn = socket_secure.connect(prudp_addr).await.unwrap(); - - loop { - let Some(conn) = socket_secure.accept().await else { - error!("server crashed"); - return; - }; + let remote = + new_rmc_gateway_connection(conn, |r| OnlyRemote::::new(r)); - info!("new connected user!"); + let v = remote + .login_ex("1469690705".to_string(), Any::default()) + .await + .unwrap(); - let _ = new_rmc_gateway_connection(conn, |_| AuthClient {}); //OnlyRemote::::new + println!("got it"); +} - } - - - - /* +async fn start_servers() { #[cfg(feature = "auth")] - let auth_server = start_auth_server().await; + let auth_server = start_auth().await; #[cfg(feature = "secure")] - let secure_server = start_secure_server().await; - let web_server = web::start_web().await; + let secure_server = start_secure().await; + //let web_server = web::start_web().await; + + //tokio::time::sleep(Duration::from_secs(1)).await; + + //start_test().await; #[cfg(feature = "auth")] - auth_server.join_handle.await.expect("auth server crashed"); + auth_server.await.expect("auth server crashed"); #[cfg(feature = "secure")] - secure_server.join_handle.await.expect("auth server crashed"); - web_server.await.expect("webserver crashed");*/ -} \ No newline at end of file + secure_server.await.expect("auth server crashed"); + //web_server.await.expect("webserver crashed"); +} diff --git a/src/nex/auth_handler.rs b/src/nex/auth_handler.rs new file mode 100644 index 0000000..d0b5e2e --- /dev/null +++ b/src/nex/auth_handler.rs @@ -0,0 +1,167 @@ +use crate::grpc::account; +use crate::kerberos::{derive_key, KerberosDateTime, Ticket}; +use crate::nex::account::Account; +use crate::rmc::protocols::auth::{Auth, RawAuth, RawAuthInfo, RemoteAuth}; +use crate::rmc::response::ErrorCode; +use crate::rmc::response::ErrorCode::Core_Unknown; +use crate::rmc::structures::any::Any; +use crate::rmc::structures::connection_data::ConnectionData; +use crate::rmc::structures::qresult::QResult; +use crate::rmc::structures::RmcSerialize; +use crate::{define_rmc_proto, kerberos, rmc}; +use macros::rmc_struct; + +define_rmc_proto!( + proto AuthClientProtocol{ + Auth + } +); + +#[rmc_struct(AuthClientProtocol)] +pub struct AuthHandler { + pub destination_server_acct: &'static Account, + pub build_name: &'static str, + pub station_url: &'static str, +} + +pub fn generate_ticket( + source_act_login_data: (u32, [u8; 16]), + dest_act_login_data: (u32, [u8; 16]), +) -> Box<[u8]> { + let source_key = derive_key(source_act_login_data.0, source_act_login_data.1); + let dest_key = derive_key(dest_act_login_data.0, dest_act_login_data.1); + + let internal_data = kerberos::TicketInternalData::new(source_act_login_data.0); + + let encrypted_inner = internal_data.encrypt(dest_key); + let encrypted_session_ticket = Ticket { + pid: dest_act_login_data.0, + session_key: internal_data.session_key, + } + .encrypt(source_key, &encrypted_inner); + + encrypted_session_ticket +} + +async fn get_login_data_by_pid(pid: u32) -> Option<(u32, [u8; 16])> { + let Ok(mut client) = account::Client::new().await else { + return None; + }; + + let Ok(passwd) = client.get_nex_password(pid).await else { + return None; + }; + + Some((pid, passwd)) +} + +impl Auth for AuthHandler { + async fn login(&self, name: String) -> Result<(), ErrorCode> { + todo!() + } + + async fn login_ex( + &self, + name: String, + extra_data: Any, + ) -> Result<(QResult, u32, Vec, ConnectionData, String), ErrorCode> { + let Ok(pid) = name.parse() else { + return Err(ErrorCode::Core_InvalidArgument); + }; + + let Ok(mut client) = account::Client::new().await else { + return Err(ErrorCode::Core_Exception); + }; + + let Ok(passwd) = client.get_nex_password(pid).await else { + return Err(ErrorCode::Core_Exception); + }; + + let source_login_data = (pid, passwd); + let destination_login_data = self.destination_server_acct.get_login_data(); + + let ticket = generate_ticket(source_login_data, destination_login_data); + + let result = QResult::success(Core_Unknown); + + let connection_data = ConnectionData { + station_url: self.station_url.to_string(), + special_station_url: "".to_string(), + //date_time: KerberosDateTime::new(1,1,1,1,1,1), + date_time: KerberosDateTime::now(), + special_protocols: Vec::new(), + }; + + Ok(( + result, + source_login_data.0, + ticket.into(), + connection_data, + self.build_name.to_owned(), + )) + } + + async fn request_ticket( + &self, + source_pid: u32, + destination_pid: u32, + ) -> Result<(QResult, Vec), ErrorCode> { + let Some(source_login_data) = get_login_data_by_pid(source_pid).await else { + return Err(ErrorCode::Core_Exception); + }; + + let desgination_login_data = if destination_pid == self.destination_server_acct.pid { + self.destination_server_acct.get_login_data() + } else { + let Some(login) = get_login_data_by_pid(destination_pid).await else { + return Err(ErrorCode::Core_Exception); + }; + login + }; + + let result = QResult::success(Core_Unknown); + + let ticket = generate_ticket(source_login_data, desgination_login_data); + + Ok((result, ticket.into())) + } + + async fn get_pid(&self, username: String) -> Result { + Err(ErrorCode::Core_Exception) + } + + async fn get_name(&self, pid: u32) -> Result { + Err(ErrorCode::Core_Exception) + } +} + +#[cfg(test)] +mod test { + use crate::rmc::structures::connection_data::ConnectionData; + use crate::rmc::structures::qresult::QResult; + use crate::rmc::structures::RmcSerialize; + use crate::rmc::response::RMCResponse; + use std::io::Cursor; + + #[test] + fn test() { + + let stuff = hex::decode("200100000a0106000000028000000100010051b3995774000000a6321c7f78847c1c5e9fb825eb26bd91841f1a40d92fc694159666119cb13527f1463ac48ad42a63e6613ede67041554b1770978112e6f1f3e177a2bfc75933216dbe38f70133a1eb28e2ae32a4b5c4b0c3e3efd4c02907992e259b257270b57a9dbe7792f4721b07f8fafb9e32d50f2555c616a015c0000004b007072756470733a2f5049443d323b7369643d313b73747265616d3d31303b747970653d323b616464726573733d322e3234332e39352e3131333b706f72743d31303030313b4349443d3100000000000100002c153ba51f00000033006272616e63683a6f726967696e2f70726f6a6563742f7775702d61676d6a206275696c643a335f385f31355f323030345f3000").unwrap(); + let stuff = RMCResponse::new(&mut Cursor::new(stuff)).unwrap(); + + let crate::rmc::response::RMCResponseResult::Success { call_id, method_id, data: stuff} = stuff.response_result else { + panic!() + }; + + + + // let stuff = hex::decode("0100010051B399577400000085F1736FCFBE93660275A3FE36FED6C2EFC57222AC99A9219CF54170A415B02DF1463AC48AD42A6307813FDE67041554B177097832ED000F892D9551A09F88E9CB0388DC1BC9527CC7384556A3287B2A349ABBF7E34A5A3EC14C2287CC7F78DA616BC3B03A035347FBD2E9A505C8EF42447CD809015F0000004E007072756470733A2F73747265616D3D31303B747970653D323B616464726573733D3139322E3136382E3137382E3132303B706F72743D31303030313B4349443D313B5049443D323B7369643D310000000000010000CDF53AA51F00000033006272616E63683A6F726967696E2F70726F6A6563742F7775702D61676D6A206275696C643A335F385F31355F323030345F3000").unwrap(); + // let stuff = hex::decode("0100010051b399577400000037d3d4814d2b16dd546c94a75d32637b45f856b5abe73cf26cfaa235c5f2c1cef1463ac48ad42a637d873fde67041554b177097880cfa7e10bb810eaf686bfb0a0cf3d65b1f476ebc046d0855327986f557dca14fbb8594883c186b863f2206f22baa0309dbcc81da2f883cb2cdc12628ec7fced015c0000004b007072756470733a2f5049443d323b7369643d313b73747265616d3d31303b747970653d323b616464726573733d322e3234332e39352e3131333b706f72743d31303030313b4349443d310000000000010000b7f33aa51f00000033006272616e63683a6f726967696e2f70726f6a6563742f7775702d61676d6a206275696c643a335f385f31355f323030345f3000").unwrap(); + + let data = <(QResult, u32, Vec, ConnectionData, String) as RmcSerialize>::deserialize( + &mut Cursor::new(stuff), + ).unwrap(); + + println!("data: {:?}", data); + } +} diff --git a/src/nex/mod.rs b/src/nex/mod.rs index 3ce1632..20e508c 100644 --- a/src/nex/mod.rs +++ b/src/nex/mod.rs @@ -1 +1,2 @@ -pub mod account; \ No newline at end of file +pub mod account; +pub mod auth_handler; \ No newline at end of file diff --git a/src/prudp/mod.rs b/src/prudp/mod.rs index 41f721c..966523e 100644 --- a/src/prudp/mod.rs +++ b/src/prudp/mod.rs @@ -3,5 +3,6 @@ pub mod router; pub mod socket; mod auth_module; pub mod sockaddr; -//pub mod secure; -pub mod station_url; \ No newline at end of file +pub mod station_url; +pub mod secure; +pub mod unsecure; \ No newline at end of file diff --git a/src/prudp/router.rs b/src/prudp/router.rs index 16424c6..8621236 100644 --- a/src/prudp/router.rs +++ b/src/prudp/router.rs @@ -50,8 +50,7 @@ impl Router { trace!("got valid prudp packet from someone({}): \n{:?}", addr, packet); let connection = packet.source_sockaddr(addr); - - println!("data from {:?}", connection); + let endpoints = self.endpoints.read().await; diff --git a/src/prudp/secure.rs b/src/prudp/secure.rs index 2689963..486500f 100644 --- a/src/prudp/secure.rs +++ b/src/prudp/secure.rs @@ -4,10 +4,13 @@ use log::error; use rc4::cipher::StreamCipherCoreWrapper; use rc4::{KeyInit, Rc4, Rc4Core, StreamCipher}; use rc4::consts::U16; +use typenum::U5; use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; use crate::kerberos::{derive_key, TicketInternalData}; use crate::nex::account::Account; -use crate::prudp::socket::EncryptionPair; +use crate::prudp::packet::PRUDPPacket; +use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance, EncryptionPair}; +use crate::prudp::unsecure::UnsecureInstance; use crate::rmc::structures::RmcSerialize; pub fn read_secure_connection_data(data: &[u8], act: &Account) -> Option<([u8; 32], u32, u32)>{ @@ -97,4 +100,93 @@ pub fn generate_secure_encryption_pairs(mut session_key: [u8; 32], count: u8) -> } vec +} + + +pub struct Secure(pub &'static str, pub &'static Account); + + +pub struct SecureInstance { + access_key: &'static str, + session_key: [u8; 32], + streams: Vec>>, + self_signature: [u8; 16], + remote_signature: [u8; 16], + pid: u32, +} + +impl CryptoHandler for Secure { + type CryptoConnectionInstance = SecureInstance; + + fn instantiate( + &self, + remote_signature: [u8; 16], + self_signature: [u8; 16], + payload: &[u8], + substream_count: u8, + ) -> Option<(Vec, Self::CryptoConnectionInstance)> { + let (session_key, pid, check_value) = read_secure_connection_data(payload, &self.1)?; + + let check_value_response = check_value + 1; + + let data = bytemuck::bytes_of(&check_value_response); + + let mut response = Vec::new(); + + data.serialize(&mut response).ok()?; + + let encryption_pairs = generate_secure_encryption_pairs(session_key, substream_count); + + Some(( + response, + SecureInstance { + pid, + streams: encryption_pairs, + session_key, + access_key: self.0, + remote_signature, + self_signature, + }, + )) + } + + fn sign_pre_handshake(&self, packet: &mut PRUDPPacket) { + packet.set_sizes(); + packet.calculate_and_assign_signature(self.0, None, None); + } +} + + +impl CryptoHandlerConnectionInstance for SecureInstance { + type Encryption = Rc4; + + fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]) { + if let Some(crypt_pair) = self.streams.get_mut(substream as usize){ + crypt_pair.recv.apply_keystream(data); + } + } + + fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]) { + if let Some(crypt_pair) = self.streams.get_mut(substream as usize){ + crypt_pair.send.apply_keystream(data); + } + } + + fn get_user_id(&self) -> u32 { + self.pid + } + + fn sign_connect(&self, packet: &mut PRUDPPacket) { + packet.set_sizes(); + packet.calculate_and_assign_signature(self.access_key, None, Some(self.self_signature)); + } + + fn sign_packet(&self, packet: &mut PRUDPPacket) { + packet.set_sizes(); + packet.calculate_and_assign_signature(self.access_key, Some(self.session_key), Some(self.self_signature)); + } + + fn verify_packet(&self, packet: &PRUDPPacket) -> bool { + true + } } \ No newline at end of file diff --git a/src/prudp/socket.rs b/src/prudp/socket.rs index 6b5c6a2..5702c51 100644 --- a/src/prudp/socket.rs +++ b/src/prudp/socket.rs @@ -27,6 +27,7 @@ use tokio::net::UdpSocket; use tokio::sync::mpsc::{channel, Receiver, Sender}; use tokio::sync::{Mutex, RwLock}; use tokio_stream::Stream; +use crate::nex::account::Account; // due to the way this is designed crashing the router thread causes deadlock, sorry ;-; // (maybe i will fix that some day) @@ -39,473 +40,13 @@ pub struct EncryptionPair { } impl EncryptionPair { - fn init_both T>(func: F) -> Self { + pub fn init_both T>(func: F) -> Self { Self { recv: func(), send: func(), } } } -/* - pub async fn process_packet( - self: &Arc, - client_address: PRUDPSockAddr, - packet: &PRUDPPacket, - ) { - let conn = self.connections.read().await; - - if !conn.contains_key(&client_address) { - drop(conn); - - let mut conn = self.connections.write().await; - //only insert if we STILL dont have the connection preventing double insertion - if !conn.contains_key(&client_address) { - conn.insert( - client_address, - ( - Arc::new(Mutex::new(ConnectionData { - sock_addr: client_address, - id: random(), - signature: [0; 16], - server_signature: [0; 16], - - active_connection_data: None, - })), - Arc::new(Mutex::new(())), - ), - ); - } - drop(conn); - } else { - drop(conn); - } - - let connections = self.connections.read().await; - - let Some(conn) = connections.get(&client_address) else { - error!("connection is still not present after making sure connection is present, giving up."); - return; - }; - - let conn = conn.clone(); - - // dont keep holding the connections list unnescesarily - drop(connections); - - let mutual_exclusion_packet_handeling_mtx = conn.1.lock().await; - let mut connection = conn.0.lock().await; - - if (packet.header.types_and_flags.get_flags() & ACK) != 0 { - //todo: handle acknowledgements and resending packets propperly - println!("got ack"); - return; - } - - if (packet.header.types_and_flags.get_flags() & MULTI_ACK) != 0 { - println!("got multi ack"); - return; - } - - match packet.header.types_and_flags.get_types() { - SYN => { - println!("got syn"); - // reset heartbeat? - let mut response_packet = packet.base_response_packet(); - - response_packet.header.types_and_flags.set_types(SYN); - response_packet.header.types_and_flags.set_flag(ACK); - response_packet.header.types_and_flags.set_flag(HAS_SIZE); - - connection.signature = client_address.calculate_connection_signature(); - - response_packet - .options - .push(ConnectionSignature(connection.signature)); - - for options in &packet.options { - match options { - SupportedFunctions(functions) => response_packet - .options - .push(SupportedFunctions(*functions & 0x04)), - MaximumSubstreamId(max_substream) => response_packet - .options - .push(MaximumSubstreamId(*max_substream)), - _ => { /* ??? */ } - } - } - - response_packet.set_sizes(); - - response_packet.calculate_and_assign_signature(self.access_key, None, None); - - let mut vec = Vec::new(); - - response_packet - .write_to(&mut vec) - .expect("somehow failed to convert backet to bytes"); - - self.socket - .send_to(&vec, client_address.regular_socket_addr) - .await - .expect("failed to send data back"); - } - CONNECT => { - println!("got connect"); - let Some(MaximumSubstreamId(max_substream)) = packet - .options - .iter() - .find(|v| matches!(v, MaximumSubstreamId(_))) - else { - return; - }; - - let Some((response_data, encryption_pairs, active_secure_connection_data)) = - (self.on_connect_handler)(packet.clone(), *max_substream).await - else { - error!("invalid connection request"); - return; - }; - - connection.active_connection_data = Some(ActiveConnectionData { - encryption_pairs, - reliable_client_queue: VecDeque::new(), - reliable_client_counter: 2, - reliable_server_counter: 1, - server_session_id: packet.header.session_id, - active_secure_connection_data, - connection_id: random(), - }); - - let mut response_packet = packet.base_response_packet(); - - response_packet.payload = response_data; - - response_packet.header.types_and_flags.set_types(CONNECT); - response_packet.header.types_and_flags.set_flag(ACK); - response_packet.header.types_and_flags.set_flag(HAS_SIZE); - - // todo: (or not) sliding windows and stuff - - response_packet.header.session_id = packet.header.session_id; - response_packet.header.sequence_id = 1; - - response_packet - .options - .push(ConnectionSignature(Default::default())); - - //let mut init_seq_id = 0; - - for option in &packet.options { - match option { - MaximumSubstreamId(max_substream) => response_packet - .options - .push(MaximumSubstreamId(*max_substream)), - SupportedFunctions(funcs) => { - response_packet.options.push(SupportedFunctions(*funcs)) - } - ConnectionSignature(sig) => connection.server_signature = *sig, - PacketOption::InitialSequenceId(_id) => { - //init_seq_id = *id; - } - _ => { /* ? */ } - } - } - - // Splatoon doesnt use compression so we arent gonna compress unless i at some point - // want to implement some server which requires it - // No encryption here for the same reason - - // todo: implement something to do secure servers - - if connection.server_signature == <[u8; 16] as Default>::default() { - error!("didn't get connection signature from client") - } - - response_packet.set_sizes(); - - response_packet.calculate_and_assign_signature( - self.access_key, - None, - Some(connection.server_signature), - ); - - let mut vec = Vec::new(); - response_packet - .write_to(&mut vec) - .expect("somehow failed to convert backet to bytes"); - - self.socket - .send_to(&vec, client_address.regular_socket_addr) - .await - .expect("failed to send data back"); - } - DATA => { - if (packet.header.types_and_flags.get_flags() & RELIABLE) != 0 { - let Some(active_connection) = connection.active_connection_data.as_mut() else { - error!("got data packet on non active connection!"); - return; - }; - - match active_connection - .reliable_client_queue - .binary_search_by_key(&packet.header.sequence_id, |p| p.header.sequence_id) - { - Ok(_) => warn!("recieved packet twice"), - Err(position) => active_connection - .reliable_client_queue - .insert(position, packet.clone()), - } - - if (packet.header.types_and_flags.get_flags() & NEED_ACK) != 0 { - let mut ack = packet.base_acknowledgement_packet(); - ack.header.session_id = active_connection.server_session_id; - - ack.set_sizes(); - let potential_session_key = connection - .active_connection_data - .as_ref() - .unwrap() - .active_secure_connection_data - .as_ref() - .map(|s| s.session_key); - - ack.calculate_and_assign_signature( - self.access_key, - potential_session_key, - Some(connection.server_signature), - ); - - let mut vec = Vec::new(); - ack.write_to(&mut vec) - .expect("somehow failed to convert backet to bytes"); - - self.socket - .send_to(&vec, client_address.regular_socket_addr) - .await - .expect("failed to send data back"); - } - drop(connection); - while let Some(mut packet) = { - let mut locked = conn.0.lock().await; - - let packet = locked - .active_connection_data - .as_mut() - .map(|a| { - a.reliable_client_queue - .front() - .is_some_and(|v| { - v.header.sequence_id == a.reliable_client_counter - }) - .then(|| a.reliable_client_queue.pop_front()) - }) - .flatten() - .flatten(); - - drop(locked); - packet - } { - if packet.options.iter().any(|v| match v { - PacketOption::FragmentId(f) => *f != 0, - _ => false, - }) { - error!("fragmented packets are unsupported right now") - } - - let mut locked = conn.0.lock().await; - - let active_connection = locked.active_connection_data.as_mut() - .expect("we litterally just recieved a packet which requires the connection to be active, failing this should be impossible"); - - active_connection.reliable_client_counter = active_connection - .reliable_client_counter - .overflowing_add(1) - .0; - - let Some(stream) = active_connection - .encryption_pairs - .get_mut(packet.header.substream_id as usize) - .map(|e| &mut e.recv) - else { - return; - }; - - stream.apply_keystream(&mut packet.payload); - - drop(locked); - // we cant divert this off to another thread we HAVE to process it now to keep order - - (self.on_data_handler)(packet, self.clone(), conn.0.clone()).await; - // ignored for now - } - } else { - error!("unreliable packets are unimplemented"); - unimplemented!() - } - //info!("{:?}", packet); - } - PING => { - let ConnectionData { - active_connection_data, - server_signature, - .. - } = &mut *connection; - - info!("got ping"); - - if (packet.header.types_and_flags.get_flags() & NEED_ACK) != 0 { - let Some(active_connection) = active_connection_data.as_mut() else { - error!("got data packet on non active connection!"); - return; - }; - - let mut ack = packet.base_acknowledgement_packet(); - ack.header.session_id = active_connection.server_session_id; - - ack.set_sizes(); - - let potential_session_key = active_connection - .active_secure_connection_data - .as_ref() - .map(|s| s.session_key); - - ack.calculate_and_assign_signature( - self.access_key, - potential_session_key, - Some(*server_signature), - ); - - let mut vec = Vec::new(); - ack.write_to(&mut vec) - .expect("somehow failed to convert backet to bytes"); - - self.socket - .send_to(&vec, client_address.regular_socket_addr) - .await - .expect("failed to send data back"); - } - } - DISCONNECT => { - println!("got disconnect"); - let Some(active_connection) = &connection.active_connection_data else { - return; - }; - - let mut ack = packet.base_acknowledgement_packet(); - - ack.header.session_id = active_connection.server_session_id; - - ack.set_sizes(); - - let potential_session_key = active_connection - .active_secure_connection_data - .as_ref() - .map(|s| s.session_key); - - ack.calculate_and_assign_signature( - self.access_key, - potential_session_key, - Some(connection.server_signature), - ); - - let mut vec = Vec::new(); - ack.write_to(&mut vec) - .expect("somehow failed to convert backet to bytes"); - - self.socket - .send_to(&vec, client_address.regular_socket_addr) - .await - .expect("failed to send data back"); - self.socket - .send_to(&vec, client_address.regular_socket_addr) - .await - .expect("failed to send data back"); - self.socket - .send_to(&vec, client_address.regular_socket_addr) - .await - .expect("failed to send data back"); - } - _ => error!( - "unimplemented packet type: {}", - packet.header.types_and_flags.get_types() - ), - } - - drop(mutual_exclusion_packet_handeling_mtx) - }*/ -/* -impl ConnectionData { - pub async fn finish_and_send_packet_to( - &mut self, - socket: &SocketData, - mut packet: PRUDPPacket, - ) { - let mut web = WEB_DATA.lock().await; - web.data.push(( - self.sock_addr.regular_socket_addr, - Outgoing(hex::encode(&packet.payload)), - )); - drop(web); - - if (packet.header.types_and_flags.get_flags() & RELIABLE) != 0 { - let Some(active_connection) = self.active_connection_data.as_mut() else { - error!("tried to send a secure packet to an inactive connection"); - return; - }; - - packet.header.sequence_id = active_connection.reliable_server_counter; - active_connection.reliable_server_counter += 1; - - let Some(encryption) = active_connection - .encryption_pairs - .get_mut(packet.header.substream_id as usize) - .map(|e| &mut e.send) - else { - return; - }; - - encryption.apply_keystream(&mut packet.payload); - } - - packet.header.session_id = self - .active_connection_data - .as_ref() - .map(|v| v.server_session_id) - .unwrap_or_default(); - - packet.header.source_port = socket.virtual_port; - packet.header.destination_port = self.sock_addr.virtual_port; - - packet.set_sizes(); - - let potential_session_key = self - .active_connection_data - .as_ref() - .unwrap() - .active_secure_connection_data - .as_ref() - .map(|s| s.session_key); - - packet.calculate_and_assign_signature( - socket.access_key, - potential_session_key, - Some(self.server_signature), - ); - let mut vec = Vec::new(); - - packet - .write_to(&mut vec) - .expect("somehow failed to convert backet to bytes"); - - if let Err(e) = socket - .socket - .send_to(&vec, self.sock_addr.regular_socket_addr) - .await - { - error!("unable to send packet to destination: {}", e); - } - } -}*/ pub struct NewEncryptionPair { pub send: E, @@ -538,9 +79,11 @@ impl Deref for InternalConnection{ impl InternalConnection{ fn next_server_count(&mut self) -> u16{ + let prev_val = self.reliable_server_counter; let (val, _) = self.reliable_server_counter.overflowing_add(1); self.reliable_server_counter = val; - val + println!("{}", prev_val); + prev_val } } @@ -645,14 +188,14 @@ impl AnyInternalConnection for InternalConne self.crypto_handler_instance.sign_packet(&mut packet); - packet.set_sizes(); - let mut vec = Vec::new(); packet .write_to(&mut vec) .expect("somehow failed to convert backet to bytes"); + println!("{}", hex::encode(&vec)); + self.socket .send_to(&vec, self.socket_addr.regular_socket_addr) .await @@ -670,8 +213,6 @@ impl InternalSocket { .write_to(&mut vec) .expect("somehow failed to convert backet to bytes"); - println!("sent out: {}", hex::encode(&vec)); - self.socket .send_to(&vec, dest.regular_socket_addr) .await @@ -824,12 +365,15 @@ impl InternalSocket { let session_id = packet.header.session_id; - let (return_data, crypto) = self.crypto_handler.instantiate( + let Some((return_data, crypto)) = self.crypto_handler.instantiate( remote_signature, *own_signature, &packet.payload, 1 + *max_substream, - ); + ) else { + error!("someone attempted to connect with invalid data"); + return; + }; let mut response = packet.base_response_packet(); response.header.types_and_flags.set_types(CONNECT); @@ -895,12 +439,6 @@ impl InternalSocket { mem::swap(&mut data, &mut packet.payload); - conn.data_sender.send(data).await.expect("socket died"); - - if packet.header.types_and_flags.get_flags() & NEED_ACK == 0{ - return; - } - let mut response = packet.base_acknowledgement_packet(); response.header.types_and_flags.set_flag(HAS_SIZE | ACK); response.header.session_id = conn.session_id; @@ -908,6 +446,10 @@ impl InternalSocket { conn.crypto_handler_instance.sign_packet(&mut response); self.send_packet_unbuffered(address, response).await; + + conn.data_sender.send(data).await.ok(); + + } async fn handle_ping(&self, address: PRUDPSockAddr, packet: PRUDPPacket){ @@ -993,6 +535,8 @@ impl AnyInternalSocket for InternalSocket { SYN => self.handle_syn(address, packet).await, CONNECT => self.handle_connect(address, packet).await, DATA => self.handle_data(address, packet).await, + DISCONNECT => self.handle_disconnect(address, packet).await, + PING => self.handle_ping(address, packet).await, _ => { error!( "unimplemented packet type: {}", @@ -1068,7 +612,7 @@ impl AnyInternalSocket for InternalSocket { return None; }; - let (_, crypt) = self.crypto_handler.instantiate(remote_signature, *own_signature, &[], 1); + let (_, crypt) = self.crypto_handler.instantiate(remote_signature, *own_signature, &[], 1)?; //todo: make this work for secure servers as well self.create_connection(crypt, address, 0).await; @@ -1130,7 +674,7 @@ pub trait CryptoHandler: Send + Sync + 'static { own_signature: [u8; 16], _: &[u8], substream_count: u8, - ) -> (Vec, Self::CryptoConnectionInstance); + ) -> Option<(Vec, Self::CryptoConnectionInstance)>; fn sign_pre_handshake(&self, packet: &mut PRUDPPacket); } @@ -1162,6 +706,7 @@ impl ExternalConnection{ impl SendingConnection{ pub async fn send(&self, data: Vec) -> Option<()> { + println!("{}", hex::encode(&data)); let internal = self.inernal.upgrade()?; let mut internal = internal.lock().await; @@ -1169,81 +714,4 @@ impl SendingConnection{ internal.send_data_packet(data).await; Some(()) } -} - -pub struct Unsecure(pub &'static str); - -pub struct UnsecureInstance { - key: &'static str, - streams: Vec>>, - self_signature: [u8; 16], - remote_signature: [u8; 16], -} - -// my hand was forced to use lazy so that we can guarantee this code -// only runs once and so that i can put it here as a "constant" (for performance and readability) -// since for some reason rust crypto doesn't have any const time key initialization -static DEFAULT_KEY: Lazy> = Lazy::new(|| Key::from(*b"CD&ML")); - -impl CryptoHandler for Unsecure { - type CryptoConnectionInstance = UnsecureInstance; - - fn instantiate( - &self, - remote_signature: [u8; 16], - self_signature: [u8; 16], - _: &[u8], - substream_count: u8, - ) -> (Vec, Self::CryptoConnectionInstance) { - ( - Vec::new(), - UnsecureInstance { - streams: (0..substream_count) - .map(|_| EncryptionPair::init_both(|| Rc4::new(&DEFAULT_KEY))) - .collect(), - key: self.0, - remote_signature, - self_signature, - }, - ) - } - - fn sign_pre_handshake(&self, packet: &mut PRUDPPacket) { - packet.set_sizes(); - packet.calculate_and_assign_signature(self.0, None, None); - } -} - -impl CryptoHandlerConnectionInstance for UnsecureInstance { - type Encryption = Rc4; - - fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]) { - if let Some(crypt_pair) = self.streams.get_mut(substream as usize){ - crypt_pair.recv.apply_keystream(data); - } - } - - fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]) { - if let Some(crypt_pair) = self.streams.get_mut(substream as usize){ - crypt_pair.send.apply_keystream(data); - } - } - - fn get_user_id(&self) -> u32 { - 0 - } - - fn sign_connect(&self, packet: &mut PRUDPPacket) { - packet.set_sizes(); - packet.calculate_and_assign_signature(self.key, None, Some(self.self_signature)); - } - - fn sign_packet(&self, packet: &mut PRUDPPacket) { - packet.set_sizes(); - packet.calculate_and_assign_signature(self.key, None, Some(self.self_signature)); - } - - fn verify_packet(&self, packet: &PRUDPPacket) -> bool { - true - } -} +} \ No newline at end of file diff --git a/src/prudp/unsecure.rs b/src/prudp/unsecure.rs new file mode 100644 index 0000000..12e15ca --- /dev/null +++ b/src/prudp/unsecure.rs @@ -0,0 +1,84 @@ +use once_cell::sync::Lazy; +use rc4::{Key, KeyInit, Rc4, StreamCipher}; +use typenum::U5; +use crate::prudp::packet::PRUDPPacket; +use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance, EncryptionPair}; + +pub struct Unsecure(pub &'static str); + + + +pub struct UnsecureInstance { + key: &'static str, + streams: Vec>>, + self_signature: [u8; 16], + remote_signature: [u8; 16], +} + +// my hand was forced to use lazy so that we can guarantee this code +// only runs once and so that i can put it here as a "constant" (for performance and readability) +// since for some reason rust crypto doesn't have any const time key initialization +static DEFAULT_KEY: Lazy> = Lazy::new(|| Key::from(*b"CD&ML")); + +impl CryptoHandler for Unsecure { + type CryptoConnectionInstance = UnsecureInstance; + + fn instantiate( + &self, + remote_signature: [u8; 16], + self_signature: [u8; 16], + _: &[u8], + substream_count: u8, + ) -> Option<(Vec, Self::CryptoConnectionInstance)> { + Some(( + Vec::new(), + UnsecureInstance { + streams: (0..substream_count) + .map(|_| EncryptionPair::init_both(|| Rc4::new(&DEFAULT_KEY))) + .collect(), + key: self.0, + remote_signature, + self_signature, + }, + )) + } + + fn sign_pre_handshake(&self, packet: &mut PRUDPPacket) { + packet.set_sizes(); + packet.calculate_and_assign_signature(self.0, None, None); + } +} + +impl CryptoHandlerConnectionInstance for UnsecureInstance { + type Encryption = Rc4; + + fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]) { + if let Some(crypt_pair) = self.streams.get_mut(substream as usize){ + crypt_pair.recv.apply_keystream(data); + } + } + + fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]) { + if let Some(crypt_pair) = self.streams.get_mut(substream as usize){ + crypt_pair.send.apply_keystream(data); + } + } + + fn get_user_id(&self) -> u32 { + 0 + } + + fn sign_connect(&self, packet: &mut PRUDPPacket) { + packet.set_sizes(); + packet.calculate_and_assign_signature(self.key, None, Some(self.self_signature)); + } + + fn sign_packet(&self, packet: &mut PRUDPPacket) { + packet.set_sizes(); + packet.calculate_and_assign_signature(self.key, None, Some(self.self_signature)); + } + + fn verify_packet(&self, packet: &PRUDPPacket) -> bool { + true + } +} \ No newline at end of file diff --git a/src/rmc/protocols/mod.rs b/src/rmc/protocols/mod.rs index 38430df..4640592 100644 --- a/src/rmc/protocols/mod.rs +++ b/src/rmc/protocols/mod.rs @@ -34,7 +34,7 @@ pub enum RemoteCallError { ServerError(ErrorCode), #[error("Connection broke")] ConnectionBroke, - #[error("Error reading response data")] + #[error("Error reading response data: {0}")] InvalidResponse(#[from] structures::Error), } @@ -148,7 +148,7 @@ macro_rules! define_rmc_proto { $($protocol:path),* }) => { paste::paste!{ - trait []: std::any::Any $( + [] + $protocol)* { + pub trait []: std::any::Any $( + [] + $protocol)* { async fn rmc_call(&self, remote_response_connection: &crate::prudp::socket::SendingConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec){ match protocol_id{ $( @@ -159,7 +159,7 @@ macro_rules! define_rmc_proto { } } - struct [](crate::rmc::protocols::RmcConnection); + pub struct [](crate::rmc::protocols::RmcConnection); impl crate::rmc::protocols::RemoteInstantiatable for []{ fn new(conn: crate::rmc::protocols::RmcConnection) -> Self{ @@ -235,7 +235,7 @@ async fn handle_incoming( return }; - if proto_id & 0x80 == 0{ + if (proto_id & 0x80) == 0{ let Some(response) = RMCResponse::new(&mut Cursor::new(v)).display_err_or_some() else { error!("ending rmc gateway."); return @@ -260,9 +260,11 @@ async fn handle_incoming( rest_of_data } = message; + info!("got rmc request, handeling it now..."); + remote.rmc_call(&sending_conn, protocol_id, method_id, call_id, rest_of_data).await; - info!("got rmc request"); + } } } diff --git a/src/rmc/response.rs b/src/rmc/response.rs index 84bd529..f47ee66 100644 --- a/src/rmc/response.rs +++ b/src/rmc/response.rs @@ -36,7 +36,7 @@ pub struct RMCResponse { impl RMCResponse { pub fn new(stream: &mut (impl Seek + Read)) -> io::Result{ // ignore the size for now this will only be used for checking - let _: u32 = stream.read_struct(IS_BIG_ENDIAN)?; + let size: u32 = stream.read_struct(IS_BIG_ENDIAN)?; let protocol_id: u8 = stream.read_struct(IS_BIG_ENDIAN)?; @@ -54,9 +54,9 @@ impl RMCResponse { let method_id: u32 = stream.read_struct(IS_BIG_ENDIAN)?; let method_id = method_id & (!0x8000); - let mut data: Vec = Vec::new(); + let mut data: Vec = vec![0u8; (size - 2 - 4 - 4) as _]; - stream.read_to_end(&mut data)?; + stream.read(&mut data)?; RMCResponseResult::Success { @@ -154,15 +154,13 @@ pub async fn send_result( method_id: u32, call_id: u32, ) { + + println!("{}", hex::encode(result.clone().unwrap())); let response_result = match result { Ok(v) => RMCResponseResult::Success { call_id, method_id, - data: { - let mut vec = Vec::new(); - v.serialize(&mut vec).expect("serialization error"); - vec - } + data: v }, Err(e) => RMCResponseResult::Error { @@ -187,7 +185,7 @@ pub async fn send_response(connection: &SendingConnection, rmcresponse: RMCRespo //taken from kinnays error list directly #[allow(nonstandard_style)] #[repr(u32)] -#[derive(Debug, EnumTryInto)] +#[derive(Debug, EnumTryInto, Clone, Copy)] pub enum ErrorCode { Core_Unknown = 0x00010001, Core_NotImplemented = 0x00010002, diff --git a/src/rmc/structures/connection_data.rs b/src/rmc/structures/connection_data.rs index d0c27b1..1adbb20 100644 --- a/src/rmc/structures/connection_data.rs +++ b/src/rmc/structures/connection_data.rs @@ -1,30 +1,15 @@ use std::io::{Read, Write}; use bytemuck::bytes_of; +use macros::RmcSerialize; use crate::kerberos::KerberosDateTime; use crate::rmc::structures::{rmc_struct, RmcSerialize}; -#[derive(Debug)] -pub struct ConnectionData<'a>{ - pub station_url: &'a str, +#[derive(Debug, RmcSerialize)] +#[rmc_struct(1)] +pub struct ConnectionData{ + pub station_url: String, pub special_protocols: Vec, - pub special_station_url: &'a str, + pub special_station_url: String, pub date_time: KerberosDateTime } -impl<'a> RmcSerialize for ConnectionData<'a>{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - rmc_struct::write_struct(writer, 1, |v|{ - self.station_url.serialize(v).expect("unable to write station url"); - self.special_protocols.serialize(v).expect("unable to write special protocols"); - self.special_station_url.serialize(v).expect("unable to write special station url"); - v.write_all(bytes_of(&self.date_time)).expect("unable to write date time"); - - Ok(()) - }) - } - - fn deserialize(_reader: &mut dyn Read) -> crate::rmc::structures::Result { - todo!() - } -} - diff --git a/src/rmc/structures/rmc_struct.rs b/src/rmc/structures/rmc_struct.rs index 2477315..8f51a5a 100644 --- a/src/rmc/structures/rmc_struct.rs +++ b/src/rmc/structures/rmc_struct.rs @@ -17,7 +17,7 @@ pub fn write_struct(writer: &mut dyn Write, version: u8, pred: impl FnOnce(&mut (pred)(&mut scratch_space)?; - let u32_size= scratch_space.len() as u32; + let u32_size = scratch_space.len() as u32; writer.write_all(bytes_of(&u32_size))?; writer.write_all(&scratch_space)?; From 33b0391ef3384299a7e53d95e3ada12a39ee2c84 Mon Sep 17 00:00:00 2001 From: DJMrTV Date: Wed, 7 May 2025 22:57:50 +0200 Subject: [PATCH 14/14] feat: add register function --- src/main.rs | 11 +++++---- src/nex/mod.rs | 3 ++- src/nex/user.rs | 45 +++++++++++++++++++++++++++++++++++++ src/rmc/protocols/mod.rs | 1 + src/rmc/protocols/secure.rs | 12 ++++++++++ 5 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 src/nex/user.rs create mode 100644 src/rmc/protocols/secure.rs diff --git a/src/main.rs b/src/main.rs index 8f49e9b..08f3b87 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,6 +38,7 @@ use std::str::FromStr; use std::time::Duration; use std::{env, fs}; use tokio::task::JoinHandle; +use crate::nex::user::User; mod endianness; mod prudp; @@ -317,10 +318,12 @@ async fn start_secure() -> JoinHandle<()> { info!("new connected user on secure :D!"); - let _ = new_rmc_gateway_connection(conn, |_| AuthHandler { - destination_server_acct: &SECURE_SERVER_ACCOUNT, - build_name: "branch:origin/project/wup-agmj build:3_8_15_2004_0", - station_url: &SECURE_STATION_URL, + let ip = conn.socket_addr.regular_socket_addr; + let pid = conn.user_id; + + let _ = new_rmc_gateway_connection(conn, |_| User { + ip, + pid }); } }) diff --git a/src/nex/mod.rs b/src/nex/mod.rs index 20e508c..623d6d8 100644 --- a/src/nex/mod.rs +++ b/src/nex/mod.rs @@ -1,2 +1,3 @@ pub mod account; -pub mod auth_handler; \ No newline at end of file +pub mod auth_handler; +pub mod user; \ No newline at end of file diff --git a/src/nex/user.rs b/src/nex/user.rs new file mode 100644 index 0000000..77ebfb6 --- /dev/null +++ b/src/nex/user.rs @@ -0,0 +1,45 @@ +use std::net::{Ipv4Addr, SocketAddrV4}; +use macros::rmc_struct; +use crate::define_rmc_proto; +use crate::prudp::station_url::{nat_types, StationUrl}; +use crate::prudp::station_url::Type::PRUDPS; +use crate::prudp::station_url::UrlOptions::{Address, NatFiltering, NatMapping, NatType, Port, PrincipalID, RVConnectionID}; +use crate::rmc::protocols::secure::{RemoteAuth, RawAuthInfo, RawAuth, Auth}; +use crate::rmc::response::ErrorCode; +use crate::rmc::structures::qresult::QResult; + +define_rmc_proto!( + proto UserProtocol{ + Auth + } +); + +#[rmc_struct(UserProtocol)] +pub struct User { + pub pid: u32, + pub ip: SocketAddrV4, +} + +impl Auth for User{ + async fn register(&self, station_urls: Vec) -> Result<(QResult, u32, String), ErrorCode> { + let public_station = StationUrl{ + url_type: PRUDPS, + options: vec![ + RVConnectionID(0), + Address(*self.ip.ip()), + Port(self.ip.port()), + NatFiltering(0), + NatMapping(0), + NatType(nat_types::BEHIND_NAT), + PrincipalID(self.pid), + ] + }; + + let result = QResult::success(ErrorCode::Core_Unknown); + + Ok((result, 0, public_station.to_string())) + } +} + + + diff --git a/src/rmc/protocols/mod.rs b/src/rmc/protocols/mod.rs index 4640592..fd09e63 100644 --- a/src/rmc/protocols/mod.rs +++ b/src/rmc/protocols/mod.rs @@ -1,6 +1,7 @@ #![allow(async_fn_in_trait)] pub mod auth; +pub mod secure; use crate::prudp::socket::{ExternalConnection, SendingConnection}; use crate::rmc::message::RMCMessage; diff --git a/src/rmc/protocols/secure.rs b/src/rmc/protocols/secure.rs new file mode 100644 index 0000000..fa11c35 --- /dev/null +++ b/src/rmc/protocols/secure.rs @@ -0,0 +1,12 @@ +use macros::{method_id, rmc_proto}; +use crate::prudp::station_url::StationUrl; +use crate::rmc::response::ErrorCode; +use crate::rmc::structures::any::Any; +use crate::rmc::structures::connection_data::ConnectionData; +use crate::rmc::structures::qresult::QResult; + +#[rmc_proto(11)] +pub trait Auth { + #[method_id(1)] + async fn register(&self, station_urls: Vec) -> Result<(QResult, u32, String), ErrorCode>; +}