Merge pull request 'nex account support' (#42) from friends/3ds into v0
All checks were successful
Build and Test / minecraft-wiiu (push) Successful in 4m54s
Build and Test / puyopuyo (push) Successful in 4m54s
Build and Test / splatoon (push) Successful in 5m4s
Build and Test / splatoon-testfire (push) Successful in 5m5s
Build and Test / wii-u-chat (push) Successful in 5m6s
Build and Test / wii-sports-club (push) Successful in 5m10s
Build and Test / mario-tennis (push) Successful in 5m13s
Build and Test / fast-racing-neo (push) Successful in 5m15s
Build and Test / friends (push) Successful in 5m17s
Build and Test / sonic-transformed (push) Successful in 5m21s
Build and Test / super-mario-maker (push) Successful in 5m35s
All checks were successful
Build and Test / minecraft-wiiu (push) Successful in 4m54s
Build and Test / puyopuyo (push) Successful in 4m54s
Build and Test / splatoon (push) Successful in 5m4s
Build and Test / splatoon-testfire (push) Successful in 5m5s
Build and Test / wii-u-chat (push) Successful in 5m6s
Build and Test / wii-sports-club (push) Successful in 5m10s
Build and Test / mario-tennis (push) Successful in 5m13s
Build and Test / fast-racing-neo (push) Successful in 5m15s
Build and Test / friends (push) Successful in 5m17s
Build and Test / sonic-transformed (push) Successful in 5m21s
Build and Test / super-mario-maker (push) Successful in 5m35s
Reviewed-on: #42
This commit is contained in:
commit
bc856feaf2
14 changed files with 1325 additions and 246 deletions
710
Cargo.lock
generated
710
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -9,6 +9,18 @@ sonic-transformed:
|
|||
RNEX_VIRTUAL_PORT_SECURE: "1:10"
|
||||
RNEX_DEFAULT_PORT: 10000
|
||||
RNEX_ACCESS_KEY: "b26a3421"
|
||||
ac-new-leaf:
|
||||
include-in-checkall: true
|
||||
features:
|
||||
- prudpv1
|
||||
- datastore
|
||||
- v3-10-22
|
||||
settings:
|
||||
AUTH_REPORT_VERSION: "branch:origin/release/ngs/3.10.x.200x build:3_10_22_2006_0"
|
||||
RNEX_VIRTUAL_PORT_INSECURE: "1:10"
|
||||
RNEX_VIRTUAL_PORT_SECURE: "1:10"
|
||||
RNEX_DEFAULT_PORT: 10000
|
||||
RNEX_ACCESS_KEY: "d6f08b40"
|
||||
wii-sports-club:
|
||||
include-in-checkall: true
|
||||
features:
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ urlencoding = "2.1.3"
|
|||
futures = "0.3.32"
|
||||
async-trait = "0.1.89"
|
||||
ctor = "1.0.7"
|
||||
nex-account = { version = "0.2.1", registry = "spbr" }
|
||||
tonic = "0.14.6"
|
||||
|
||||
[dev-dependencies]
|
||||
# criterion = "0.7.0"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
use crate::grpc::account::Error::SomethingHappened;
|
||||
use json::{JsonValue, object};
|
||||
use nex_account::grpc::Pid;
|
||||
use nex_account::grpc::nex_account_service_client::NexAccountServiceClient;
|
||||
use once_cell::sync::Lazy;
|
||||
use rnex_core::PID;
|
||||
use std::array::TryFromSliceError;
|
||||
use std::ops::Deref;
|
||||
use std::sync::LazyLock;
|
||||
use std::{env, result};
|
||||
use thiserror::Error;
|
||||
use tokio::task::{JoinError, spawn_blocking};
|
||||
use tonic::transport::Channel;
|
||||
|
||||
static API_KEY: Lazy<String> = Lazy::new(|| {
|
||||
let key = env::var("ACCOUNT_GQL_API_KEY").expect("no graphql ip specified");
|
||||
|
||||
|
|
@ -26,8 +31,10 @@ pub enum Error {
|
|||
RequestError(#[from] ureq::Error),
|
||||
#[error(transparent)]
|
||||
Json(#[from] json::Error),
|
||||
//#[error(transparent)]
|
||||
//Status(#[from] tonic::Status),
|
||||
#[error(transparent)]
|
||||
Status(#[from] tonic::Status),
|
||||
#[error(transparent)]
|
||||
Transport(#[from] tonic::transport::Error),
|
||||
#[error("invalid password size: {0}")]
|
||||
PasswordConversion(#[from] TryFromSliceError),
|
||||
#[error("something happened")]
|
||||
|
|
@ -38,141 +45,101 @@ pub enum Error {
|
|||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
pub struct Client; //(reqwest::Client);
|
||||
static NEX_ACCOUNT_URL: LazyLock<String> =
|
||||
LazyLock::new(|| env::var("NEX_ACCOUNT_ENDPOINT").expect("NEX_ACCOUNT_ENDPOINT not set"));
|
||||
|
||||
pub struct Client(NexAccountServiceClient<Channel>); //(reqwest::Client);
|
||||
|
||||
impl Client {
|
||||
pub async fn new() -> Result<Self> {
|
||||
//Ok(Self(reqwest::ClientBuilder::new().build()?))
|
||||
Ok(Self)
|
||||
let client = NexAccountServiceClient::connect(NEX_ACCOUNT_URL.as_str()).await?;
|
||||
Ok(Self(client))
|
||||
}
|
||||
|
||||
async fn do_request(&self, request_data: JsonValue) -> Result<JsonValue> {
|
||||
let request = ureq::post(CLIENT_URI.as_str())
|
||||
.header("X-API-Key", API_KEY.deref())
|
||||
.content_type("application/json");
|
||||
let mut response = spawn_blocking(move || request.send(request_data.to_string())).await??;
|
||||
pub async fn get_nex_key(&mut self, pid: PID) -> Result<[u8; 16]> {
|
||||
let prekey = self.0.get_nex_key_by_pid(Pid { pid }).await?.into_inner();
|
||||
|
||||
let str_body = response.body_mut().read_to_string()?;
|
||||
Ok(json::parse(&str_body)?)
|
||||
/*
|
||||
let mut request = reqwest::Request::new(Method::POST, Url::from_str(CLIENT_URI.as_str()).unwrap());
|
||||
log::warn!("prekey is {:?}", prekey);
|
||||
|
||||
*(request.body_mut()) = Some(Body::from(request_data.to_string()));
|
||||
request.headers_mut().insert("X-API-Key", HeaderValue::from_str(&API_KEY).unwrap());
|
||||
request.headers_mut().insert("Content-Type", HeaderValue::from_str("application/json").unwrap());
|
||||
let nexkey: [u8; 16] = prekey
|
||||
.key
|
||||
.try_into()
|
||||
.map_err(|_| Error::SomethingHappened)?;
|
||||
|
||||
let response = self.0.execute(request).await?;
|
||||
|
||||
Ok(json::parse(&response.text().await?)?)
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
pub async fn get_nex_password(&mut self, pid: PID) -> Result<[u8; 16]> {
|
||||
let req = self
|
||||
.do_request(object! {
|
||||
"query": r"query($pid: Int!){
|
||||
userByPid(pid: $pid){
|
||||
nexPassword
|
||||
}
|
||||
}",
|
||||
"variables": {
|
||||
"pid": pid
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
let Some(val) = req
|
||||
.entries()
|
||||
.find(|v| v.0 == "data")
|
||||
.ok_or(SomethingHappened)?
|
||||
.1
|
||||
.entries()
|
||||
.find(|v| v.0 == "userByPid")
|
||||
.ok_or(SomethingHappened)?
|
||||
.1
|
||||
.entries()
|
||||
.find(|v| v.0 == "nexPassword")
|
||||
.ok_or(SomethingHappened)?
|
||||
.1
|
||||
.as_str()
|
||||
else {
|
||||
return Err(SomethingHappened);
|
||||
};
|
||||
|
||||
Ok(val.as_bytes().try_into().map_err(|_| SomethingHappened)?)
|
||||
Ok(nexkey)
|
||||
}
|
||||
|
||||
pub async fn get_user_level(&mut self, pid: PID) -> Result<i32> {
|
||||
let req = self
|
||||
.do_request(object! {
|
||||
"query": r"query($pid: Int!){
|
||||
userByPid(pid: $pid){
|
||||
accountLevel
|
||||
}
|
||||
}",
|
||||
"variables": {
|
||||
"pid": pid
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
// let req = self
|
||||
// .do_request(object! {
|
||||
// "query": r"query($pid: Int!){
|
||||
// userByPid(pid: $pid){
|
||||
// accountLevel
|
||||
// }
|
||||
// }",
|
||||
// "variables": {
|
||||
// "pid": pid
|
||||
// }
|
||||
// })
|
||||
// .await?;
|
||||
//
|
||||
// let Some(val) = req
|
||||
// .entries()
|
||||
// .find(|v| v.0 == "data")
|
||||
// .ok_or(SomethingHappened)?
|
||||
// .1
|
||||
// .entries()
|
||||
// .find(|v| v.0 == "userByPid")
|
||||
// .ok_or(SomethingHappened)?
|
||||
// .1
|
||||
// .entries()
|
||||
// .find(|v| v.0 == "accountLevel")
|
||||
// .ok_or(SomethingHappened)?
|
||||
// .1
|
||||
// .as_i32()
|
||||
// else {
|
||||
// return Err(SomethingHappened);
|
||||
// };
|
||||
|
||||
let Some(val) = req
|
||||
.entries()
|
||||
.find(|v| v.0 == "data")
|
||||
.ok_or(SomethingHappened)?
|
||||
.1
|
||||
.entries()
|
||||
.find(|v| v.0 == "userByPid")
|
||||
.ok_or(SomethingHappened)?
|
||||
.1
|
||||
.entries()
|
||||
.find(|v| v.0 == "accountLevel")
|
||||
.ok_or(SomethingHappened)?
|
||||
.1
|
||||
.as_i32()
|
||||
else {
|
||||
return Err(SomethingHappened);
|
||||
};
|
||||
|
||||
Ok(val)
|
||||
// everyone is tester until this is implemented
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
pub async fn get_pid_from_token(&mut self, token: String) -> Result<PID> {
|
||||
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_i32()
|
||||
else {
|
||||
return Err(SomethingHappened);
|
||||
};
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
// pub async fn get_pid_from_token(&mut self, token: String) -> Result<PID> {
|
||||
// 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_i32()
|
||||
// else {
|
||||
// return Err(SomethingHappened);
|
||||
// };
|
||||
//
|
||||
// Ok(val)
|
||||
// }
|
||||
|
||||
/*pub async fn get_user_data(&mut self , pid: u32) -> Result<GetUserDataResponse>{
|
||||
let req = Request::new(GetUserDataRequest{
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use cfg_if::cfg_if;
|
|||
use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc};
|
||||
use hmac::Hmac;
|
||||
use hmac::Mac;
|
||||
use md5::digest::generic_array::GenericArray;
|
||||
use md5::{Digest, Md5};
|
||||
use rc4::KeyInit;
|
||||
use rc4::cipher::StreamCipherCoreWrapper;
|
||||
|
|
@ -28,24 +29,6 @@ pub const SESSION_KEY_LENGTH: usize = SessionLengthTy::USIZE;
|
|||
|
||||
type Md5Hmac = Hmac<md5::Md5>;
|
||||
|
||||
pub fn derive_key(pid: PID, password: &[u8]) -> [u8; 16] {
|
||||
let iteration_count = 65000 + pid % 1024;
|
||||
// we do one iteration out here to ensure the key is always 16 bytes
|
||||
|
||||
let mut key: [u8; 16] = {
|
||||
let mut md5 = Md5::new();
|
||||
md5.update(password);
|
||||
md5.finalize().try_into().unwrap()
|
||||
};
|
||||
|
||||
for _ in 1..iteration_count {
|
||||
let mut md5 = Md5::new();
|
||||
md5.update(key);
|
||||
key = md5.finalize().try_into().unwrap();
|
||||
}
|
||||
|
||||
key
|
||||
}
|
||||
#[derive(Pod, Zeroable, Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct KerberosDateTime(pub u64);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use macros::RmcSerialize;
|
||||
use md5::{Digest, Md5};
|
||||
|
||||
use rnex_core::PID;
|
||||
|
||||
|
|
@ -6,27 +7,42 @@ use rnex_core::PID;
|
|||
pub struct Account {
|
||||
pub pid: PID,
|
||||
pub username: String,
|
||||
pub kerbros_password: Box<[u8]>,
|
||||
pub nex_key: [u8; 16],
|
||||
}
|
||||
|
||||
impl Account {
|
||||
pub fn new(pid: PID, username: &str, passwd: &str) -> Self {
|
||||
let iteration_count = 65000 + pid % 1024;
|
||||
// we do one iteration out here to ensure the key is always 16 bytes
|
||||
|
||||
let mut key: [u8; 16] = {
|
||||
let mut md5 = Md5::new();
|
||||
md5.update(passwd);
|
||||
md5.finalize().try_into().unwrap()
|
||||
};
|
||||
|
||||
for _ in 1..iteration_count {
|
||||
let mut md5 = Md5::new();
|
||||
md5.update(key);
|
||||
key = md5.finalize().try_into().unwrap();
|
||||
}
|
||||
|
||||
Self {
|
||||
kerbros_password: passwd.as_bytes().into(),
|
||||
nex_key: key.into(),
|
||||
username: username.into(),
|
||||
pid,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_raw_password(pid: PID, username: &str, passwd: &[u8]) -> Self {
|
||||
pub fn new_raw_key(pid: PID, username: &str, nex_key: [u8; 16]) -> Self {
|
||||
Self {
|
||||
kerbros_password: passwd.into(),
|
||||
username: username.into(),
|
||||
pid,
|
||||
nex_key,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_login_data(&self) -> (PID, &[u8]) {
|
||||
(self.pid, &self.kerbros_password)
|
||||
pub fn get_login_data(&self) -> (PID, [u8; 16]) {
|
||||
(self.pid, self.nex_key)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use cfg_if::cfg_if;
|
|||
use log::{info, warn};
|
||||
use macros::rmc_struct;
|
||||
use rnex_core::PID;
|
||||
use rnex_core::kerberos::{KerberosDateTime, Ticket, derive_key};
|
||||
use rnex_core::kerberos::{KerberosDateTime, Ticket};
|
||||
use rnex_core::nex::account::Account;
|
||||
use rnex_core::rmc::protocols::OnlyRemote;
|
||||
use rnex_core::rmc::protocols::auth::{Auth, RawAuth, RawAuthInfo, RemoteAuth};
|
||||
|
|
@ -34,11 +34,11 @@ pub struct AuthHandler {
|
|||
}
|
||||
|
||||
pub fn generate_ticket(
|
||||
source_act_login_data: (PID, &[u8]),
|
||||
dest_act_login_data: (PID, &[u8]),
|
||||
source_act_login_data: (PID, [u8; 16]),
|
||||
dest_act_login_data: (PID, [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 source_key = source_act_login_data.1;
|
||||
let dest_key = dest_act_login_data.1;
|
||||
|
||||
let internal_data = kerberos::TicketInternalData::new(source_act_login_data.0);
|
||||
|
||||
|
|
@ -53,12 +53,12 @@ pub fn generate_ticket(
|
|||
}
|
||||
pub fn generate_ticket_with_string_user_key(
|
||||
source_act: PID,
|
||||
dest_act_login_data: (PID, &[u8]),
|
||||
dest_act_login_data: (PID, [u8; 16]),
|
||||
) -> (String, Box<[u8]>) {
|
||||
let source_key: [u8; 8] = rand::random();
|
||||
let key_string = hex::encode(source_key);
|
||||
let key_data: [u8; 16] = key_string.as_bytes().try_into().unwrap();
|
||||
let dest_key = derive_key(dest_act_login_data.0, dest_act_login_data.1);
|
||||
let dest_key = dest_act_login_data.1;
|
||||
|
||||
let internal_data = kerberos::TicketInternalData::new(source_act);
|
||||
|
||||
|
|
@ -72,22 +72,22 @@ pub fn generate_ticket_with_string_user_key(
|
|||
(key_string, encrypted_session_ticket)
|
||||
}
|
||||
|
||||
async fn get_login_data_by_pid(pid: PID) -> Option<(PID, Box<[u8]>)> {
|
||||
async fn get_login_data_by_pid(pid: PID) -> Option<(PID, [u8; 16])> {
|
||||
if pid == GUEST_ACCOUNT.pid {
|
||||
let source_login_data = GUEST_ACCOUNT.get_login_data();
|
||||
|
||||
return Some((source_login_data.0, source_login_data.1.into()));
|
||||
return Some((source_login_data.0, source_login_data.1));
|
||||
}
|
||||
|
||||
let Ok(mut client) = account::Client::new().await else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let Ok(passwd) = client.get_nex_password(pid).await else {
|
||||
let Ok(passwd) = client.get_nex_key(pid).await else {
|
||||
return None;
|
||||
};
|
||||
|
||||
Some((pid, passwd.into()))
|
||||
Some((pid, passwd))
|
||||
}
|
||||
|
||||
fn station_url_from_sock_addr(sock_addr: SocketAddrV4) -> String {
|
||||
|
|
@ -109,6 +109,7 @@ impl AuthHandler {
|
|||
#[cfg(feature = "guest_login")]
|
||||
{
|
||||
if name == GUEST_ACCOUNT.username {
|
||||
log::info!("guest account login");
|
||||
let source_login_data = GUEST_ACCOUNT.get_login_data();
|
||||
let destination_login_data = self.destination_server_acct.get_login_data();
|
||||
|
||||
|
|
@ -118,25 +119,31 @@ impl AuthHandler {
|
|||
));
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("parsing pid");
|
||||
let Ok(pid) = name.parse() else {
|
||||
warn!("unable to connect to parse pid: {}", name);
|
||||
return Err(ErrorCode::Core_InvalidArgument);
|
||||
};
|
||||
|
||||
log::info!("creating account grpc client");
|
||||
let Ok(mut client) = account::Client::new().await else {
|
||||
warn!("unable to connect to grpc");
|
||||
return Err(ErrorCode::Core_Exception);
|
||||
};
|
||||
|
||||
let Ok(passwd) = client.get_nex_password(pid).await else {
|
||||
log::info!("grabbing nex key");
|
||||
let Ok(passwd) = client.get_nex_key(pid).await else {
|
||||
warn!("unable to get nex password for pid: {}:", pid);
|
||||
return Err(ErrorCode::Core_Exception);
|
||||
};
|
||||
|
||||
let source_login_data = (pid, &passwd[..]);
|
||||
log::info!("source login data");
|
||||
let source_login_data = (pid, passwd);
|
||||
println!("{}, {:?}", pid, passwd);
|
||||
let destination_login_data = self.destination_server_acct.get_login_data();
|
||||
|
||||
log::info!("we are a-ok here");
|
||||
Ok((
|
||||
pid,
|
||||
generate_ticket(source_login_data, destination_login_data),
|
||||
|
|
@ -322,7 +329,7 @@ impl Auth for AuthHandler {
|
|||
|
||||
let result = QResult::success(Core_Unknown);
|
||||
|
||||
let ticket = generate_ticket((pid, &passwd[..]), desgination_login_data);
|
||||
let ticket = generate_ticket((pid, passwd), desgination_login_data);
|
||||
|
||||
Ok((result, ticket.into()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1810,4 +1810,112 @@ impl DataStore for User {
|
|||
|
||||
Ok((ranking_results, q_results))
|
||||
}
|
||||
|
||||
async fn ctr_pickup_course_search_object(&self, course_search_param: DataStoreSearchParam, extra_data: Vec<String>) -> Result<Vec<DataStoreCustomRankingResult>, ErrorCode> {
|
||||
let mut courses = Vec::new();
|
||||
|
||||
let mut stream = sqlx::query!(
|
||||
r#"
|
||||
SELECT
|
||||
object.data_id,
|
||||
object.owner,
|
||||
object.size,
|
||||
object.name,
|
||||
object.data_type,
|
||||
object.meta_binary,
|
||||
object.permission,
|
||||
object.permission_recipients,
|
||||
object.delete_permission,
|
||||
object.delete_permission_recipients,
|
||||
object.period,
|
||||
object.refer_data_id,
|
||||
object.flag,
|
||||
object.tags,
|
||||
object.creation_date,
|
||||
object.update_date,
|
||||
ranking.value
|
||||
FROM (
|
||||
SELECT * FROM datastore.objects object
|
||||
WHERE
|
||||
object.upload_completed = TRUE AND
|
||||
object.deleted = FALSE AND
|
||||
object.under_review = FALSE
|
||||
) object
|
||||
JOIN (
|
||||
SELECT data_id, value
|
||||
FROM datastore.object_custom_rankings ranking
|
||||
WHERE ranking.application_id = 0
|
||||
) ranking
|
||||
ON
|
||||
object.data_id = ranking.data_id
|
||||
ORDER BY RANDOM()
|
||||
LIMIT 100
|
||||
"#
|
||||
)
|
||||
.fetch(get_db());
|
||||
|
||||
while let Some(row) = stream.try_next().await.map_err(|e| {
|
||||
eprintln!("stream error: {:?}", e);
|
||||
ErrorCode::DataStore_NotFound
|
||||
})? {
|
||||
let permission = Permission {
|
||||
permission: row.permission.unwrap_or(0) as u8,
|
||||
recipient_ids: row.permission_recipients.unwrap_or_default(),
|
||||
};
|
||||
|
||||
let del_permission = Permission {
|
||||
permission: row.delete_permission.unwrap_or(0) as u8,
|
||||
recipient_ids: row.delete_permission_recipients.unwrap_or_default(),
|
||||
};
|
||||
|
||||
let meta_binary = row.meta_binary.map(QBuffer).unwrap_or_default();
|
||||
|
||||
let created_time = row
|
||||
.creation_date
|
||||
.map(KerberosDateTime::from_naive)
|
||||
.unwrap_or_default();
|
||||
|
||||
let updated_time = row
|
||||
.update_date
|
||||
.map(KerberosDateTime::from_naive)
|
||||
.unwrap_or_default();
|
||||
|
||||
let referred_time = row
|
||||
.creation_date
|
||||
.map(KerberosDateTime::from_naive)
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut meta_info = GetMetaInfo {
|
||||
dataid: row.data_id,
|
||||
owner: row.owner.unwrap_or(0),
|
||||
size: row.size.unwrap_or(0) as u32,
|
||||
name: row.name,
|
||||
data_type: row.data_type.unwrap_or(0) as u16,
|
||||
meta_binary,
|
||||
permission,
|
||||
del_permission,
|
||||
period: row.period.unwrap_or(0) as u16,
|
||||
status: 0,
|
||||
referred_count: 0,
|
||||
refer_dat_id: row.refer_data_id.unwrap_or(0) as u32,
|
||||
flag: row.flag.unwrap_or(0) as u32,
|
||||
tags: row.tags.unwrap_or_default(),
|
||||
expire_time: KerberosDateTime::PRACTICALLY_NEVER,
|
||||
created_time,
|
||||
updated_time,
|
||||
referred_time,
|
||||
ratings: get_rating_with_slot_data_id(row.data_id).await?,
|
||||
};
|
||||
|
||||
let course = DataStoreCustomRankingResult {
|
||||
order: 0,
|
||||
score: row.value.unwrap_or(0) as u32,
|
||||
meta_info,
|
||||
};
|
||||
|
||||
courses.push(course);
|
||||
}
|
||||
|
||||
Ok(courses)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,21 @@
|
|||
use std::env;
|
||||
use std::io::{Cursor, Write};
|
||||
use std::ops::Deref;
|
||||
use std::sync::Weak;
|
||||
use std::sync::{Arc, atomic::AtomicU32};
|
||||
use std::sync::{LazyLock, Weak};
|
||||
|
||||
use bytemuck::bytes_of;
|
||||
use base64::{Engine as _, engine::general_purpose};
|
||||
use bytemuck::{Pod, Zeroable, bytes_of};
|
||||
use hex::decode;
|
||||
use hmac::Mac;
|
||||
use log::info;
|
||||
use macros::rmc_struct;
|
||||
use rnex_core::rmc::protocols::account_management::{
|
||||
AccountManagement, RawAccountManagement, RawAccountManagementInfo, RemoteAccountManagement,
|
||||
AccountExtraInfo, AccountManagement, RawAccountManagement, RawAccountManagementInfo,
|
||||
RemoteAccountManagement,
|
||||
};
|
||||
use rnex_core::rmc::protocols::friends_3ds::{
|
||||
Friends3DS, RawFriends3DS, RawFriends3DSInfo, RemoteFriends3DS,
|
||||
};
|
||||
use rnex_core::rmc::protocols::friends_wiiu::{
|
||||
FriendsWiiU, RawFriendsWiiU, RawFriendsWiiUInfo, RemoteFriendsWiiU,
|
||||
|
|
@ -49,10 +56,19 @@ use rnex_core::rmc::structures::data::Data;
|
|||
|
||||
use crate::executables::common::get_db;
|
||||
|
||||
use crate::rmc::protocols::friends_3ds::{
|
||||
FriendComment, FriendMii, FriendMiiList, FriendPersistentInfo, FriendPicture, FriendPresence,
|
||||
FriendRelationship, Mii, MiiList, MyProfile, NintendoPresence, PlayedGame,
|
||||
};
|
||||
use nex_account::grpc::ActCreateInfo;
|
||||
use nex_account::grpc::nex_account_service_client::NexAccountServiceClient;
|
||||
use nex_account::{derive_pid_hmac, grpc_client};
|
||||
|
||||
define_rmc_proto!(
|
||||
proto FriendsUser{
|
||||
Secure,
|
||||
FriendsWiiU
|
||||
FriendsWiiU,
|
||||
Friends3DS
|
||||
}
|
||||
);
|
||||
define_rmc_proto!(
|
||||
|
|
@ -67,11 +83,22 @@ define_rmc_proto!(
|
|||
}
|
||||
);
|
||||
|
||||
static NEX_ACCOUNT_URL: LazyLock<String> =
|
||||
LazyLock::new(|| env::var("NEX_ACCOUNT_ENDPOINT").expect("NEX_ACCOUNT_ENDPOINT not set"));
|
||||
|
||||
pub struct UserData {
|
||||
info: NNAInfo,
|
||||
presence: NintendoPresenceV2,
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Pod, Zeroable, Copy, Clone, Debug)]
|
||||
pub struct NascToken {
|
||||
pub pid: i32,
|
||||
pub time: [u8; 14],
|
||||
pub pwd_hash: [u8; 4],
|
||||
}
|
||||
|
||||
#[rmc_struct(FriendsUser)]
|
||||
pub struct FriendsUser {
|
||||
pub fm: Arc<FriendsManager>,
|
||||
|
|
@ -100,6 +127,250 @@ impl FriendsManager {
|
|||
}
|
||||
}
|
||||
|
||||
// ALL of this is stubbed
|
||||
impl Friends3DS for FriendsUser {
|
||||
async fn update_profile(&self, profile: MyProfile) -> Result<(), ErrorCode> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_mii(&self, profile: Mii) -> Result<(), ErrorCode> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_mii_list(&self, profile: MiiList) -> Result<(), ErrorCode> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_played_games(&self, profile: Vec<PlayedGame>) -> Result<(), ErrorCode> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_preference(
|
||||
&self,
|
||||
show_online_status: bool,
|
||||
show_current_title: bool,
|
||||
block_friend_requests: bool,
|
||||
) -> Result<(), ErrorCode> {
|
||||
// stubbed
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_friend_mii(
|
||||
&self,
|
||||
friends: Vec<crate::rmc::protocols::friends_3ds::FriendInfo>,
|
||||
) -> Result<Vec<FriendMii>, ErrorCode> {
|
||||
// sorry for the copying pretendo but i don't have a mii on hand rn
|
||||
let data: Vec<u8> = vec![
|
||||
0x03, 0x00, 0x00, 0x40, 0xE9, 0x55, 0xA2, 0x09, 0xE7, 0xC7, 0x41, 0x82, 0xD9, 0x7D,
|
||||
0x0B, 0x2D, 0x03, 0xB3, 0xB8, 0x8D, 0x27, 0xD9, 0x00, 0x00, 0x01, 0x40, 0x62, 0x00,
|
||||
0x65, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x12, 0x00, 0x81, 0x01, 0x04, 0x68, 0x43, 0x18,
|
||||
0x20, 0x34, 0x46, 0x14, 0x81, 0x12, 0x17, 0x68, 0x0D, 0x00, 0x00, 0x29, 0x03, 0x52,
|
||||
0x48, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x86,
|
||||
];
|
||||
|
||||
let dummymii = FriendMii {
|
||||
data: Data {},
|
||||
pid: 69,
|
||||
mii: Mii {
|
||||
data: Data {},
|
||||
name: "test".to_string(),
|
||||
profanity: false,
|
||||
char_set: 0,
|
||||
mii_data: data,
|
||||
},
|
||||
modified_at: Default::default(),
|
||||
};
|
||||
|
||||
Ok(vec![dummymii])
|
||||
}
|
||||
|
||||
async fn get_friend_mii_list(
|
||||
&self,
|
||||
friends: Vec<crate::rmc::protocols::friends_3ds::FriendInfo>,
|
||||
) -> Result<Vec<FriendMiiList>, ErrorCode> {
|
||||
Err(ErrorCode::Core_NotImplemented)
|
||||
}
|
||||
|
||||
async fn is_active_game(
|
||||
&self,
|
||||
unk: Vec<u32>,
|
||||
game_key: crate::rmc::protocols::friends_3ds::GameKey,
|
||||
) -> Result<Vec<u32>, ErrorCode> {
|
||||
Err(ErrorCode::Core_NotImplemented)
|
||||
}
|
||||
|
||||
async fn get_principal_id_by_local_friend_code(
|
||||
&self,
|
||||
unk1: u64,
|
||||
unk2: Vec<u64>,
|
||||
) -> Result<Vec<FriendRelationship>, ErrorCode> {
|
||||
Err(ErrorCode::Core_NotImplemented)
|
||||
}
|
||||
|
||||
async fn get_friend_relationships(
|
||||
&self,
|
||||
unk2: Vec<u32>,
|
||||
) -> Result<Vec<FriendRelationship>, ErrorCode> {
|
||||
let dummy = FriendRelationship {
|
||||
data: Data {},
|
||||
pid: 69,
|
||||
local_friend_code: 3268487429723707977,
|
||||
relationship_type: 1,
|
||||
};
|
||||
|
||||
Ok(vec![dummy])
|
||||
}
|
||||
|
||||
async fn add_friend_by_pid(&self, unk: u64, pid: PID) -> Result<FriendRelationship, ErrorCode> {
|
||||
Err(ErrorCode::Core_NotImplemented)
|
||||
}
|
||||
|
||||
async fn add_friend_by_lst_pid(
|
||||
&self,
|
||||
unk: u64,
|
||||
pid: Vec<PID>,
|
||||
) -> Result<Vec<FriendRelationship>, ErrorCode> {
|
||||
Err(ErrorCode::Core_NotImplemented)
|
||||
}
|
||||
|
||||
async fn remove_friend_by_local_code(&self, local_code: u64) -> Result<(), ErrorCode> {
|
||||
Err(ErrorCode::Core_NotImplemented)
|
||||
}
|
||||
|
||||
async fn remove_friend_by_pid(&self, pid: PID) -> Result<(), ErrorCode> {
|
||||
Err(ErrorCode::Core_NotImplemented)
|
||||
}
|
||||
|
||||
async fn get_all_friends(&self) -> Result<Vec<FriendRelationship>, ErrorCode> {
|
||||
let dummy = FriendRelationship {
|
||||
data: Data {},
|
||||
pid: 69,
|
||||
local_friend_code: 3268487429723707977,
|
||||
relationship_type: 1,
|
||||
};
|
||||
|
||||
Ok(vec![dummy])
|
||||
}
|
||||
|
||||
async fn update_blacklist(&self) -> Result<(), ErrorCode> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn sync_friend(
|
||||
&self,
|
||||
unk1: u64,
|
||||
unk2: Vec<u32>,
|
||||
unk3: Vec<u64>,
|
||||
) -> Result<Vec<FriendRelationship>, ErrorCode> {
|
||||
log::info!("params: {:?}, {:?}, {:?}", unk1, unk2, unk3);
|
||||
|
||||
let dummy = FriendRelationship {
|
||||
data: Data {},
|
||||
pid: 69,
|
||||
local_friend_code: 3268487429723707977,
|
||||
relationship_type: 1,
|
||||
};
|
||||
|
||||
Ok(vec![dummy])
|
||||
}
|
||||
|
||||
async fn update_presence(
|
||||
&self,
|
||||
nintendo_presence: NintendoPresence,
|
||||
unk: bool,
|
||||
) -> Result<(), ErrorCode> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_favorite_game_key(
|
||||
&self,
|
||||
game_key: rnex_core::rmc::protocols::friends_3ds::GameKey,
|
||||
) -> Result<(), ErrorCode> {
|
||||
log::info!("favorite game key: {:?}", game_key);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_comment(&self, comment: String) -> Result<(), ErrorCode> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_picture(&self, unk: u32, picture: Vec<u8>) -> Result<(), ErrorCode> {
|
||||
Err(ErrorCode::Core_NotImplemented)
|
||||
}
|
||||
|
||||
async fn get_friend_presence(&self, unk: Vec<u32>) -> Result<Vec<FriendPresence>, ErrorCode> {
|
||||
log::info!("pids: {:?}", unk);
|
||||
|
||||
let presence = FriendPresence {
|
||||
data: Data {},
|
||||
pid: 69,
|
||||
presence: NintendoPresence {
|
||||
data: Data {},
|
||||
changed_bit_flag: 0xFFFFFFFF,
|
||||
game_key: rnex_core::rmc::protocols::friends_3ds::GameKey {
|
||||
data: Data {},
|
||||
title_id: 1125899907457280,
|
||||
version: 2064,
|
||||
},
|
||||
game_mode_desctiption: "".to_string(),
|
||||
join_availibility_flag: 0,
|
||||
mm_system_type: 0,
|
||||
join_game_id: 0,
|
||||
join_game_mode: 0,
|
||||
owner_pid: 0,
|
||||
join_group_id: 0,
|
||||
application_arg: vec![],
|
||||
},
|
||||
};
|
||||
|
||||
Ok(vec![presence])
|
||||
}
|
||||
|
||||
async fn get_friend_comment(
|
||||
&self,
|
||||
unk: Vec<crate::rmc::protocols::friends_3ds::FriendInfo>,
|
||||
) -> Result<Vec<FriendComment>, ErrorCode> {
|
||||
Err(ErrorCode::Core_NotImplemented)
|
||||
}
|
||||
|
||||
async fn get_friend_picture(&self, unk: Vec<u32>) -> Result<Vec<FriendPicture>, ErrorCode> {
|
||||
Err(ErrorCode::Core_NotImplemented)
|
||||
}
|
||||
|
||||
async fn get_friend_persistent_info(
|
||||
&self,
|
||||
unk: Vec<u32>,
|
||||
) -> Result<Vec<FriendPersistentInfo>, ErrorCode> {
|
||||
let dummypersistentinfo = FriendPersistentInfo {
|
||||
data: Data {},
|
||||
pid: 69,
|
||||
region: 0,
|
||||
country: 0,
|
||||
area: 0,
|
||||
language: 0,
|
||||
platform: 0,
|
||||
game_key: rnex_core::rmc::protocols::friends_3ds::GameKey {
|
||||
data: Data {},
|
||||
title_id: 1125899907457280,
|
||||
version: 2064,
|
||||
},
|
||||
message: "yo whats up".to_string(),
|
||||
msg_updated_at: KerberosDateTime::now(),
|
||||
friended_at: KerberosDateTime::now(),
|
||||
last_online: KerberosDateTime::now(),
|
||||
};
|
||||
|
||||
Ok(vec![dummypersistentinfo])
|
||||
}
|
||||
|
||||
async fn send_invitation(&self, unk: Vec<u32>) -> Result<(), ErrorCode> {
|
||||
Err(ErrorCode::Core_NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
impl FriendsWiiU for FriendsUser {
|
||||
async fn update_and_get_all_information(
|
||||
&self,
|
||||
|
|
@ -121,7 +392,27 @@ impl FriendsWiiU for FriendsUser {
|
|||
ErrorCode,
|
||||
> {
|
||||
// let query = query!("select ", self.pid).fetch_all(get_db()).await;
|
||||
Err(ErrorCode::Core_NotImplemented)
|
||||
Ok((
|
||||
PrincipalPreference {
|
||||
data: Data {},
|
||||
block_friend_request: false,
|
||||
show_online: true,
|
||||
show_playing_title: false,
|
||||
},
|
||||
Comment {
|
||||
data: Data {},
|
||||
last_changed: KerberosDateTime::now(),
|
||||
message: "stub(will be impl'd later)".into(),
|
||||
unk: 0,
|
||||
},
|
||||
vec![],
|
||||
vec![],
|
||||
vec![],
|
||||
vec![],
|
||||
false,
|
||||
vec![],
|
||||
false,
|
||||
))
|
||||
}
|
||||
|
||||
async fn add_friend(&self, friend: PID) -> Result<(FriendRequest, FriendInfo), ErrorCode> {
|
||||
|
|
@ -288,28 +579,51 @@ 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 nex_token = if let Ok(extra_info) = auth_data.try_get_as::<AccountExtraInfo>() {
|
||||
extra_info.nex_token
|
||||
} else if let Ok(data) = auth_data.try_get_as::<NintendoCreateAccountData>() {
|
||||
data.nex_token
|
||||
} else {
|
||||
return Err(ErrorCode::Authentication_InvalidParam);
|
||||
};
|
||||
let (pid, nex_key) = nex_account::decode_nexact_token(&nex_token).map_err(|e| {
|
||||
log::error!("failed to decode token: {}", e);
|
||||
log::info!("{:?}", nex_token);
|
||||
ErrorCode::Authentication_InvalidParam
|
||||
})?;
|
||||
|
||||
let Ok(mut mac) = HMacMd5::new_from_slice(key.as_bytes()) else {
|
||||
return Err(ErrorCode::Authentication_InvalidParam);
|
||||
};
|
||||
//let mac = derive_pid_hmac(data.nna_info.principal_basic_info.pid, &nexkey);
|
||||
|
||||
mac.write_all(bytes_of(&pid))
|
||||
.expect("failed to write to hmac???");
|
||||
let mac = mac.finalize().into_bytes();
|
||||
let mut client = grpc_client().await.map_err(|e| {
|
||||
eprintln!("error occurred: {:?}", e);
|
||||
ErrorCode::Core_Unknown
|
||||
})?;
|
||||
|
||||
let hex_str = hex::encode(mac);
|
||||
let new_account: ActCreateInfo = ActCreateInfo {
|
||||
principal_name,
|
||||
key: nex_key.into(),
|
||||
email,
|
||||
pid,
|
||||
};
|
||||
|
||||
return Ok((pid, hex_str));
|
||||
let nexkey = client
|
||||
.create_new_sequential_or_update_and_get_account(new_account)
|
||||
.await
|
||||
.map_err(|e| ErrorCode::Core_Unknown)?
|
||||
.into_inner();
|
||||
|
||||
if nexkey.key.len() != 16 {
|
||||
log::error!("nex key was not 16 bytes long");
|
||||
return Err(ErrorCode::Authentication_InvalidParam);
|
||||
}
|
||||
Err(ErrorCode::Core_NotImplemented)
|
||||
|
||||
let nexkeyarray: [u8; 16] = nexkey.key.try_into().expect("how...?");
|
||||
|
||||
let mac = derive_pid_hmac(pid, &nexkeyarray);
|
||||
|
||||
let hex_str = hex::encode(mac);
|
||||
|
||||
return Ok((pid, hex_str));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,27 +45,33 @@ use tokio::sync::mpsc::Sender;
|
|||
use cfg_if::cfg_if;
|
||||
use log::{error, info};
|
||||
use macros::rmc_struct;
|
||||
use rnex_core::prudp::socket_addr::PRUDPSockAddr;
|
||||
use rnex_core::rmc::protocols::message_delivery::{
|
||||
MessageDelivery, RawMessageDelivery, RawMessageDeliveryInfo, RemoteMessageDelivery,
|
||||
use rnex_core::{
|
||||
prudp::socket_addr::PRUDPSockAddr,
|
||||
rmc::{
|
||||
protocols::{
|
||||
message_delivery::{
|
||||
MessageDelivery, RawMessageDelivery, RawMessageDeliveryInfo, RemoteMessageDelivery,
|
||||
RemoteMessageDeliveryNoResponse,
|
||||
},
|
||||
messaging::UserMessage,
|
||||
notifications::{NotificationEvent, RemoteNotification},
|
||||
ranking::{
|
||||
CompetitionRankingGetParam, CompetitionRankingScoreData,
|
||||
CompetitionRankingScoreInfo,
|
||||
},
|
||||
},
|
||||
response::ErrorCode::{Core_InvalidArgument, RendezVous_AccountExpired},
|
||||
structures::{
|
||||
matchmake::{Gathering, MatchmakeSessionSearchCriteria},
|
||||
qbuffer::QBuffer,
|
||||
qresult::QResult,
|
||||
ranking::UploadCompetitionData,
|
||||
},
|
||||
},
|
||||
};
|
||||
use rnex_core::rmc::protocols::notifications::{NotificationEvent, RemoteNotification};
|
||||
use rnex_core::rmc::protocols::ranking::{
|
||||
CompetitionRankingGetParam, CompetitionRankingScoreData, CompetitionRankingScoreInfo,
|
||||
};
|
||||
use rnex_core::rmc::response::ErrorCode::{Core_InvalidArgument, RendezVous_AccountExpired};
|
||||
use rnex_core::rmc::structures::qbuffer::QBuffer;
|
||||
use rnex_core::rmc::structures::qresult::QResult;
|
||||
use rnex_core::rmc::structures::ranking::UploadCompetitionData;
|
||||
use std::sync::{Arc, Weak};
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
|
||||
use crate::kerberos::Ticket;
|
||||
use crate::rmc::protocols::message_delivery::RemoteMessageDeliveryNoResponse;
|
||||
use crate::rmc::protocols::messaging::UserMessage;
|
||||
use crate::rmc::structures::matchmake::Gathering;
|
||||
use crate::rmc::structures::matchmake::MatchmakeSessionSearchCriteria;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "datastore")] {
|
||||
use rnex_core::rmc::protocols::datastore::{DataStore, RawDataStore, RawDataStoreInfo, RemoteDataStore};
|
||||
|
|
@ -91,7 +97,8 @@ cfg_if! {
|
|||
Matchmake,
|
||||
NatTraversal,
|
||||
Utility,
|
||||
Ranking
|
||||
Ranking,
|
||||
MessageDelivery
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -864,6 +871,15 @@ impl Utility for User {
|
|||
async fn acquire_nex_unique_id(&self) -> Result<u64, ErrorCode> {
|
||||
return Ok(rand::random());
|
||||
}
|
||||
|
||||
async fn get_integer_settings(&self, index: u32) -> Result<Vec<(u16, i32)>, ErrorCode> {
|
||||
Ok(vec![
|
||||
(0, 1),
|
||||
(1, 2),
|
||||
(2, 0),
|
||||
(3, 4)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
impl Ranking for User {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use typenum::U16;
|
|||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
|
||||
use crate::{
|
||||
kerberos::{SESSION_KEY_LENGTH, SessionLengthTy, TicketInternalData, derive_key},
|
||||
kerberos::{SESSION_KEY_LENGTH, SessionLengthTy, TicketInternalData},
|
||||
nex::account::Account,
|
||||
rmc::structures::RmcSerialize,
|
||||
};
|
||||
|
|
@ -26,7 +26,7 @@ pub fn read_secure_connection_data(
|
|||
|
||||
let ticket_data = &mut ticket_data[0..ticket_data_size - 0x10];
|
||||
|
||||
let server_key = derive_key(act.pid, &act.kerbros_password[..]);
|
||||
let server_key = act.nex_key;
|
||||
|
||||
let mut rc4: StreamCipherCoreWrapper<Rc4Core<U16>> =
|
||||
Rc4::new_from_slice(&server_key).expect("unable to init rc4 keystream");
|
||||
|
|
|
|||
|
|
@ -453,6 +453,12 @@ pub trait DataStore {
|
|||
&self,
|
||||
application_id: u32,
|
||||
) -> Result<bool, ErrorCode>;
|
||||
#[method_id(82)]
|
||||
async fn ctr_pickup_course_search_object(
|
||||
&self,
|
||||
course_search_param: DataStoreSearchParam,
|
||||
extra_data: Vec<String>,
|
||||
) -> Result<Vec<DataStoreCustomRankingResult>, ErrorCode>;
|
||||
#[method_id(87)]
|
||||
async fn report_course(
|
||||
&self,
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ pub struct MyProfile {
|
|||
pub struct Mii {
|
||||
#[extends]
|
||||
pub data: Data,
|
||||
pub unk1: String,
|
||||
pub unk2: bool,
|
||||
pub unk3: u8,
|
||||
pub name: String,
|
||||
pub profanity: bool,
|
||||
pub char_set: u8, // 0 is JPN/USA/EUR, 1 is CHN, 2 is KOR and 3 is TWN
|
||||
pub mii_data: Vec<u8>,
|
||||
}
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ pub struct MiiList {
|
|||
pub mii_data: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(RmcSerialize)]
|
||||
#[derive(RmcSerialize, Debug)]
|
||||
#[rmc_struct(0)]
|
||||
pub struct GameKey {
|
||||
#[extends]
|
||||
|
|
@ -62,7 +62,7 @@ pub struct PlayedGame {
|
|||
#[derive(RmcSerialize)]
|
||||
#[rmc_struct(0)]
|
||||
pub struct FriendInfo {
|
||||
pub unk1: u32,
|
||||
pub pid: u32,
|
||||
pub unk2: KerberosDateTime,
|
||||
}
|
||||
|
||||
|
|
@ -91,9 +91,9 @@ pub struct FriendMiiList {
|
|||
pub struct FriendRelationship {
|
||||
#[extends]
|
||||
pub data: Data,
|
||||
pub unk1: u32,
|
||||
pub unk2: u64,
|
||||
pub unk3: u8,
|
||||
pub pid: u32,
|
||||
pub local_friend_code: u64,
|
||||
pub relationship_type: u8,
|
||||
}
|
||||
|
||||
#[derive(RmcSerialize)]
|
||||
|
|
@ -117,7 +117,7 @@ pub struct NintendoPresence {
|
|||
pub struct FriendPresence {
|
||||
#[extends]
|
||||
pub data: Data,
|
||||
pub unk: u32,
|
||||
pub pid: u32,
|
||||
pub presence: NintendoPresence,
|
||||
}
|
||||
#[derive(RmcSerialize)]
|
||||
|
|
|
|||
|
|
@ -6,4 +6,6 @@ use rnex_core::rmc::response::ErrorCode;
|
|||
pub trait Utility {
|
||||
#[method_id(1)]
|
||||
async fn acquire_nex_unique_id(&self) -> Result<u64, ErrorCode>;
|
||||
#[method_id(7)]
|
||||
async fn get_integer_settings(&self, index: u32) -> Result<Vec<(u16, i32)>, ErrorCode>;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue