From c5677e304da1f1d177cafd40b6e147e4605ed5e6 Mon Sep 17 00:00:00 2001 From: Maple Date: Sat, 11 Apr 2026 16:36:57 +0200 Subject: [PATCH 1/8] add more debugging logs --- prudpv0/src/server.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/prudpv0/src/server.rs b/prudpv0/src/server.rs index 1305d81..ce6cd21 100644 --- a/prudpv0/src/server.rs +++ b/prudpv0/src/server.rs @@ -284,6 +284,9 @@ impl Server { }); let mut conns = self.connections.write().await; + if conns.contains_key(&addr) { + error!("client already connected but tried to connect again"); + } conns.insert(addr, conn.clone()); drop(conns); @@ -346,7 +349,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 { From 1c8352a23536eed274e144215d7343bcd0b0537b Mon Sep 17 00:00:00 2001 From: Maple Date: Sat, 11 Apr 2026 18:16:20 +0200 Subject: [PATCH 2/8] actually delete connection if someone disconnects --- prudpv0/src/server.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/prudpv0/src/server.rs b/prudpv0/src/server.rs index ce6cd21..63fefe7 100644 --- a/prudpv0/src/server.rs +++ b/prudpv0/src/server.rs @@ -414,6 +414,10 @@ impl Server { ); drop(inner); + let mut conns = self.connections.write().await; + conns.remove(&addr); + 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; From dd4015b2c4b49d196ea29d53bbaf5c96d681fd31 Mon Sep 17 00:00:00 2001 From: Maple Date: Sat, 11 Apr 2026 20:02:10 +0200 Subject: [PATCH 3/8] tie connections to a session id + prudp socket address --- prudpv0/src/server.rs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/prudpv0/src/server.rs b/prudpv0/src/server.rs index 63fefe7..4921b3f 100644 --- a/prudpv0/src/server.rs +++ b/prudpv0/src/server.rs @@ -73,7 +73,7 @@ pub struct Server { param: ProxyStartupParam, socket: UdpSocket, crypto: C, - connections: RwLock>>>, + connections: RwLock>>>, } impl Server { @@ -167,8 +167,9 @@ 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 { + let Some(conn) = conn.upgrade() else { break }; sleep(Duration::from_secs(3)); let mut inner = conn.inner.lock().await; @@ -214,7 +215,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; } @@ -284,10 +285,10 @@ impl Server { }); let mut conns = self.connections.write().await; - if conns.contains_key(&addr) { + if conns.contains_key(&(addr, header.session_id)) { error!("client already connected but tried to connect again"); } - conns.insert(addr, conn.clone()); + conns.insert((addr, header.session_id), conn.clone()); drop(conns); spawn({ @@ -297,7 +298,7 @@ impl Server { }); spawn({ let this = self.clone(); - let conn = conn.clone(); + let conn = Arc::downgrade(&conn); this.timeout_thread(conn) }); @@ -325,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; }; @@ -372,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; }; @@ -398,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; }; @@ -415,14 +416,17 @@ impl Server { drop(inner); let mut conns = self.connections.write().await; - conns.remove(&addr); + 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); @@ -444,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); From b503363e5698cb547c8c3b90bd43f47336968d3c Mon Sep 17 00:00:00 2001 From: Maple Date: Sat, 11 Apr 2026 20:24:13 +0200 Subject: [PATCH 4/8] add check setting status --- rnex-core/src/nex/friends_handler.rs | 4 ++++ rnex-core/src/rmc/protocols/friends.rs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index be5d684..09e9c9d 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -143,6 +143,10 @@ impl Friends for FriendsUser { false, )) } + + async fn check_setting_status(&self) -> Result { + Ok(0xFF) + } } impl Secure for FriendsUser { diff --git a/rnex-core/src/rmc/protocols/friends.rs b/rnex-core/src/rmc/protocols/friends.rs index 775016e..07dcd1a 100644 --- a/rnex-core/src/rmc/protocols/friends.rs +++ b/rnex-core/src/rmc/protocols/friends.rs @@ -143,4 +143,6 @@ pub trait Friends { ), ErrorCode, >; + #[method_id(19)] + async fn check_setting_status(&self) -> Result; } From 8164317b1980259fb25d1ce9d7ded934201fd398 Mon Sep 17 00:00:00 2001 From: Maple Date: Sat, 11 Apr 2026 21:59:37 +0200 Subject: [PATCH 5/8] fix friends connection --- prudpv0/src/server.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/prudpv0/src/server.rs b/prudpv0/src/server.rs index 4921b3f..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::{ @@ -170,7 +169,7 @@ impl Server { async fn timeout_thread(self: Arc, conn: Weak>) { loop { let Some(conn) = conn.upgrade() else { break }; - sleep(Duration::from_secs(3)); + sleep(Duration::from_secs(3)).await; let mut inner = conn.inner.lock().await; if (Instant::now() - inner.last_action).as_secs() > 5 { @@ -259,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"); From 7dd6265eddff46be751098d2c4d1746ccf03420f Mon Sep 17 00:00:00 2001 From: Maple Date: Sun, 12 Apr 2026 17:10:07 +0200 Subject: [PATCH 6/8] fix guest login on backend --- rnex-core/src/executables/friends_backend.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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, + }) }); } } From 3c651e874c413650c129acfbef7c62ddc50c3344 Mon Sep 17 00:00:00 2001 From: Maple Date: Sun, 12 Apr 2026 18:05:52 +0200 Subject: [PATCH 7/8] start work on implementing account management for friends --- rnex-core/src/nex/friends_handler.rs | 20 ++++++++++++++++++- .../src/rmc/protocols/account_management.rs | 19 ++++++++++++++++-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index 09e9c9d..36d08dc 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -2,6 +2,9 @@ use std::sync::{Arc, atomic::AtomicU32}; 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::{ @@ -32,7 +35,8 @@ define_rmc_proto!( ); define_rmc_proto!( proto FriendsGuest{ - Secure + Secure, + AccountManagement } ); #[rmc_struct(FriendsUser)] @@ -198,3 +202,17 @@ 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); + 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..64bd3bc 100644 --- a/rnex-core/src/rmc/protocols/account_management.rs +++ b/rnex-core/src/rmc/protocols/account_management.rs @@ -1,4 +1,19 @@ -use macros::rmc_proto; +use macros::{method_id, rmc_proto}; + +use rnex_core::{ + PID, + rmc::{response::ErrorCode, structures::any::Any}, +}; #[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>; +} From 2fc2a0ac1ef954675ba6f42e40fa431dbaba582a Mon Sep 17 00:00:00 2001 From: Maple Date: Sun, 12 Apr 2026 18:56:08 +0200 Subject: [PATCH 8/8] implement nintendo_create_account --- rnex-core/src/grpc/account.rs | 36 +++++++++++++++++++ rnex-core/src/nex/friends_handler.rs | 30 ++++++++++++++++ .../src/rmc/protocols/account_management.rs | 13 ++++++- rnex-core/src/rmc/protocols/friends.rs | 6 ++-- 4 files changed, 81 insertions(+), 4 deletions(-) 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 36d08dc..42e5c71 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -1,5 +1,8 @@ +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::{ @@ -27,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, @@ -153,6 +159,8 @@ impl Friends for FriendsUser { } } +type HMacMd5 = hmac::Hmac; + impl Secure for FriendsUser { async fn register( &self, @@ -213,6 +221,28 @@ impl AccountManagement for FriendsGuest { 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 64bd3bc..8ef2d9e 100644 --- a/rnex-core/src/rmc/protocols/account_management.rs +++ b/rnex-core/src/rmc/protocols/account_management.rs @@ -1,10 +1,21 @@ -use macros::{method_id, 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 { #[method_id(27)] diff --git a/rnex-core/src/rmc/protocols/friends.rs b/rnex-core/src/rmc/protocols/friends.rs index 07dcd1a..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,