diff --git a/prudpv0/src/server.rs b/prudpv0/src/server.rs index 1305d81..34c7647 100644 --- a/prudpv0/src/server.rs +++ b/prudpv0/src/server.rs @@ -6,7 +6,6 @@ use std::{ Arc, LazyLock, Weak, atomic::{AtomicBool, AtomicU32}, }, - thread::sleep, time::Duration, }; @@ -30,7 +29,7 @@ use tokio::{ net::{TcpSocket, UdpSocket}, spawn, sync::{Mutex, RwLock}, - time::Instant, + time::{Instant, sleep}, }; use crate::{ @@ -73,7 +72,7 @@ pub struct Server { param: ProxyStartupParam, socket: UdpSocket, crypto: C, - connections: RwLock>>>, + connections: RwLock>>>, } impl Server { @@ -167,9 +166,10 @@ impl Server { self.clone().send_data_packet(conn.clone(), &data).await; } } - async fn timeout_thread(self: Arc, conn: Arc>) { + async fn timeout_thread(self: Arc, conn: Weak>) { loop { - sleep(Duration::from_secs(3)); + let Some(conn) = conn.upgrade() else { break }; + sleep(Duration::from_secs(3)).await; let mut inner = conn.inner.lock().await; if (Instant::now() - inner.last_action).as_secs() > 5 { @@ -214,7 +214,7 @@ impl Server { drop(inner); let mut conns = self.connections.write().await; - conns.remove(&conn.addr); + conns.remove(&(conn.addr, conn.session_id)); drop(conns); break; } @@ -258,6 +258,7 @@ impl Server { }; let pid = ci.get_user_id(); + println!("user with pid {} is connecting", pid); let buf_conn = new_backend_connection(&self.param, addr, pid).await; let Some(buf_conn) = buf_conn else { error!("unable to connect to backend"); @@ -284,7 +285,10 @@ impl Server { }); let mut conns = self.connections.write().await; - conns.insert(addr, conn.clone()); + if conns.contains_key(&(addr, header.session_id)) { + error!("client already connected but tried to connect again"); + } + conns.insert((addr, header.session_id), conn.clone()); drop(conns); spawn({ @@ -294,7 +298,7 @@ impl Server { }); spawn({ let this = self.clone(); - let conn = conn.clone(); + let conn = Arc::downgrade(&conn); this.timeout_thread(conn) }); @@ -322,7 +326,7 @@ impl Server { return; }; - let Some(res) = self.get_connection(addr).await else { + let Some(res) = self.get_connection((addr, header.session_id)).await else { warn!("data packet on inactive connection from: {:?}", addr); return; }; @@ -346,7 +350,8 @@ impl Server { ); while let Some((_, mut packet)) = { let ctr = conn.client_packet_counter; - conn.packet_queue.remove(&ctr) + let packet = conn.packet_queue.remove(&ctr); + packet } { info!("processing packet: {}", conn.client_packet_counter); let Some(payload) = packet.payload_mut() else { @@ -368,7 +373,7 @@ impl Server { info!("got ping"); let header = packet.header().unwrap(); - let Some(conn) = self.get_connection(addr).await else { + let Some(conn) = self.get_connection((addr, header.session_id)).await else { warn!("ping on inactive connection: {:?}", addr); return; }; @@ -394,7 +399,7 @@ impl Server { info!("got disconnect"); let header = packet.header().unwrap(); - let Some(conn) = self.get_connection(addr).await else { + let Some(conn) = self.get_connection((addr, header.session_id)).await else { warn!("ping on inactive connection: {:?}", addr); return; }; @@ -410,11 +415,18 @@ impl Server { ); drop(inner); + let mut conns = self.connections.write().await; + conns.remove(&(addr, header.session_id)); + drop(conns); + self.socket.send_to(&packet, addr.regular_socket_addr).await; self.socket.send_to(&packet, addr.regular_socket_addr).await; self.socket.send_to(&packet, addr.regular_socket_addr).await; } - async fn get_connection(&self, addr: PRUDPSockAddr) -> Option>> { + async fn get_connection( + &self, + addr: (PRUDPSockAddr, u8), + ) -> Option>> { let rd = self.connections.read().await; let res = rd.get(&addr).cloned(); drop(rd); @@ -436,7 +448,7 @@ impl Server { let addr = PRUDPSockAddr::new(SocketAddr::V4(addr), header.source); - if let Some(conn) = self.get_connection(addr).await { + if let Some(conn) = self.get_connection((addr, header.session_id)).await { let mut inner = conn.inner.lock().await; inner.last_action = Instant::now(); drop(inner); diff --git a/rnex-core/src/executables/friends_backend.rs b/rnex-core/src/executables/friends_backend.rs index 7ea03b4..74983f7 100644 --- a/rnex-core/src/executables/friends_backend.rs +++ b/rnex-core/src/executables/friends_backend.rs @@ -56,9 +56,11 @@ pub async fn start_friends_backend() { }) }); } else { - Arc::new_cyclic(move |this| FriendsGuest { - fm, - addr: c.prudpsock_addr, + new_rmc_gateway_connection(stream.into(), move |r| { + Arc::new_cyclic(move |this| FriendsGuest { + fm, + addr: c.prudpsock_addr, + }) }); } } diff --git a/rnex-core/src/grpc/account.rs b/rnex-core/src/grpc/account.rs index 64be4e9..d6ad140 100644 --- a/rnex-core/src/grpc/account.rs +++ b/rnex-core/src/grpc/account.rs @@ -103,6 +103,42 @@ impl Client { Ok(val.as_bytes().try_into().map_err(|_| SomethingHappened)?) } + pub async fn get_pid_from_token(&mut self, token: String) -> Result { + let req = self + .do_request(object! { + "query": + r"query($token: String!){ + token(tokenData: $token){ + pid + } + }", + "variables": { + "token": token + } + }) + .await?; + // this breaks switch nex servers and should be fixed eventually + let Some(val) = req + .entries() + .find(|v| v.0 == "data") + .ok_or(SomethingHappened)? + .1 + .entries() + .find(|v| v.0 == "token") + .ok_or(SomethingHappened)? + .1 + .entries() + .find(|v| v.0 == "pid") + .ok_or(SomethingHappened)? + .1 + .as_u32() + else { + return Err(SomethingHappened); + }; + + Ok(val) + } + /*pub async fn get_user_data(&mut self , pid: u32) -> Result{ let req = Request::new(GetUserDataRequest{ pid diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index be5d684..42e5c71 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -1,7 +1,13 @@ +use std::io::{Cursor, Write}; use std::sync::{Arc, atomic::AtomicU32}; +use bytemuck::bytes_of; +use hmac::Mac; use log::info; use macros::rmc_struct; +use rnex_core::rmc::protocols::account_management::{ + AccountManagement, RawAccountManagement, RawAccountManagementInfo, RemoteAccountManagement, +}; use rnex_core::rmc::protocols::friends::{Friends, RawFriends, RawFriendsInfo, RemoteFriends}; use rnex_core::rmc::protocols::secure::{RawSecure, RawSecureInfo, RemoteSecure, Secure}; use rnex_core::{ @@ -24,6 +30,9 @@ use rnex_core::rmc::protocols::friends::{GameKey, MiiV2, PrincipalBasicInfo}; use rnex_core::PID; +use crate::rmc::protocols::account_management::NintendoCreateAccountData; +use rnex_core::rmc::structures::RmcSerialize; + define_rmc_proto!( proto FriendsUser{ Secure, @@ -32,7 +41,8 @@ define_rmc_proto!( ); define_rmc_proto!( proto FriendsGuest{ - Secure + Secure, + AccountManagement } ); #[rmc_struct(FriendsUser)] @@ -143,8 +153,14 @@ impl Friends for FriendsUser { false, )) } + + async fn check_setting_status(&self) -> Result { + Ok(0xFF) + } } +type HMacMd5 = hmac::Hmac; + impl Secure for FriendsUser { async fn register( &self, @@ -194,3 +210,39 @@ impl Secure for FriendsGuest { Err(ErrorCode::Core_NotImplemented) } } + +impl AccountManagement for FriendsGuest { + async fn nintendo_create_account( + &self, + principal_name: String, + key: String, + groups: u32, + email: String, + auth_data: Any, + ) -> Result<(PID, String), ErrorCode> { + println!("{}, {}, {}, {}", principal_name, key, groups, email); + if auth_data.name == "NintendoCreateAccountData" { + let Ok(data) = + NintendoCreateAccountData::deserialize(&mut Cursor::new(&auth_data.data)) + else { + return Err(ErrorCode::Authentication_InvalidParam); + }; + + let pid = data.nna_info.principal_basic_info.pid; + info!("create account: {}", pid); + + let Ok(mut mac) = HMacMd5::new_from_slice(key.as_bytes()) else { + return Err(ErrorCode::Authentication_InvalidParam); + }; + + mac.write_all(bytes_of(&pid)) + .expect("failed to write to hmac???"); + let mac = mac.finalize().into_bytes(); + + let hex_str = hex::encode(mac); + + return Ok((pid, hex_str)); + } + Err(ErrorCode::Core_NotImplemented) + } +} diff --git a/rnex-core/src/rmc/protocols/account_management.rs b/rnex-core/src/rmc/protocols/account_management.rs index 541d1f6..8ef2d9e 100644 --- a/rnex-core/src/rmc/protocols/account_management.rs +++ b/rnex-core/src/rmc/protocols/account_management.rs @@ -1,4 +1,30 @@ -use macros::rmc_proto; +use macros::{RmcSerialize, method_id, rmc_proto}; + +use rnex_core::{ + PID, + rmc::{response::ErrorCode, structures::any::Any}, +}; + +use crate::{kerberos::KerberosDateTime, rmc::protocols::friends::NNAInfo}; + +#[derive(RmcSerialize, Debug, Clone)] +#[rmc_struct(0)] +pub struct NintendoCreateAccountData { + pub nna_info: NNAInfo, + pub nex_token: String, + pub birthday: KerberosDateTime, + pub unk: u64, +} #[rmc_proto(25)] -pub trait AccountManagement {} +pub trait AccountManagement { + #[method_id(27)] + async fn nintendo_create_account( + &self, + principal_name: String, + key: String, + groups: u32, + email: String, + auth_data: Any, + ) -> Result<(PID, String), ErrorCode>; +} diff --git a/rnex-core/src/rmc/protocols/friends.rs b/rnex-core/src/rmc/protocols/friends.rs index 775016e..3f6138f 100644 --- a/rnex-core/src/rmc/protocols/friends.rs +++ b/rnex-core/src/rmc/protocols/friends.rs @@ -2,7 +2,7 @@ use macros::{RmcSerialize, method_id, rmc_proto}; use rnex_core::{kerberos::KerberosDateTime, rmc::response::ErrorCode}; -#[derive(RmcSerialize)] +#[derive(RmcSerialize, Debug, Clone)] #[rmc_struct(0)] pub struct MiiV2 { pub name: String, @@ -12,7 +12,7 @@ pub struct MiiV2 { pub date_time: KerberosDateTime, } -#[derive(RmcSerialize)] +#[derive(RmcSerialize, Debug, Clone)] #[rmc_struct(0)] pub struct PrincipalBasicInfo { pub pid: u32, @@ -21,7 +21,7 @@ pub struct PrincipalBasicInfo { pub unk: u8, } -#[derive(RmcSerialize)] +#[derive(RmcSerialize, Debug, Clone)] #[rmc_struct(0)] pub struct NNAInfo { pub principal_basic_info: PrincipalBasicInfo, @@ -143,4 +143,6 @@ pub trait Friends { ), ErrorCode, >; + #[method_id(19)] + async fn check_setting_status(&self) -> Result; }