progress
Some checks failed
Build and Test / super-mario-maker (push) Failing after 10m30s
Build and Test / fast-racing-neo (push) Failing after 11m40s
Build and Test / friends (push) Failing after 11m56s
Build and Test / wii-sports-club (push) Failing after 12m9s
Build and Test / splatoon (push) Failing after 12m14s
Build and Test / puyopuyo (push) Failing after 12m23s
Build and Test / wii-u-chat (push) Failing after 12m23s
Build and Test / mario-tennis (push) Failing after 12m23s
Build and Test / sonic-transformed (push) Failing after 12m23s
Build and Test / splatoon-testfire (push) Failing after 12m30s
Build and Test / minecraft-wiiu (push) Failing after 12m37s

This commit is contained in:
Maple Nebel 2026-07-12 18:21:18 +02:00
commit 4ff0a5efcc
187 changed files with 4526 additions and 5132 deletions

1430
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,38 @@
[workspace] [workspace]
resolver = "3" resolver = "3"
members = [ members = [
"macros",
"rnex-core",
"prudpv1", "prudpv1",
"prudpv0" "prudpv0",
, "proxy", "proxy-common", "prudplite"] "proxy",
"proxy-common",
"rnex-rmc",
"rnex-rmc/macros",
"rnex-util",
"rnex-protocols/mm-protos",
"rnex-protocols/ds-protos",
"rnex-protocols/rk-protos",
"rnex-protocols/base-protos",
"rnex-protocols/auth-protos",
"rnex-protocols/reggie-protos",
"rnex-protocols/msg-protos",
"rnex-protocols/fpd-protos",
"rnex-prudp",
"rnex-server",
"rnex-server/backend-auth",
"rnex-server/backend-secure",
"rnex-server/backend-node-holder",
"rnex-server-nex-modules/rnex-mm",
"rnex-server-nex-modules/rnex-ds",
"rnex-server-nex-modules/rnex-rk",
"rnex-server-nex-modules/rnex-fpd",
"rnex-server-nex-modules/rnex-node-holder",
"rnex-server-nex-modules/rnex-base",
"rnex-server-nex-modules/rnex-msg",
"rnex-server-nex-modules/rnex-auth"
]
[workspace.lints.clippy]
print_stdout = { level = "deny", priority = 1}
pedantic = { level = "warn", priority = 0 }
all = { level = "warn", priority = -1 }

View file

@ -13,4 +13,6 @@ echo CHECKING $EDITION
echo FEATURES: echo FEATURES:
echo $EDITION_FEATURES echo $EDITION_FEATURES
RUSTFLAGS="--deny warnings" cargo check --features "$EDITION_FEATURES" #RUSTFLAGS="--deny warnings" cargo clippy --workspace --features "$EDITION_FEATURES"
# cargo check --workspace --features "$EDITION_FEATURES"
echo "edition checks are disabled right now due to being in"

View file

@ -3,9 +3,16 @@ name = "proxy-common"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
[lints]
workspace = true
[dependencies] [dependencies]
thiserror = "2.0.12" thiserror = "2.0.12"
rnex-core = { path = "../rnex-core", version = "0.1.1" } rnex-prudp = { path = "../rnex-prudp" }
rnex-server = { path = "../rnex-server" }
rnex-util = { path = "../rnex-util" }
rnex-reggie-protos = { path = "../rnex-protocols/reggie-protos" }
rnex-rmc = { path = "../rnex-rmc" }
tokio = { version = "1.47.0", features = ["full"] } tokio = { version = "1.47.0", features = ["full"] }
log = "0.4.25"
hex = "0.4.3" hex = "0.4.3"
tracing = "0.1.44"

View file

@ -1,21 +1,14 @@
use log::{error, info}; use rnex_prudp::{socket_addr::PRUDPSockAddr, virtual_port::VirtualPort};
use rnex_core::{ use rnex_reggie_protos::reggie::{EdgeNodeHolderConnectOption, RemoteEdgeNodeHolder};
PID, use rnex_rmc::{
executables::common::try_get_ip,
prudp::{socket_addr::PRUDPSockAddr, virtual_port::VirtualPort},
reggie::{RemoteEdgeNodeHolder, UnitPacketWrite},
rmc::{
protocols::{
RemoteDisconnectable, RmcCallable, RmcConnection, RmcPureRemoteObject, RemoteDisconnectable, RmcCallable, RmcConnection, RmcPureRemoteObject,
new_rmc_gateway_connection, new_rmc_gateway_connection, serialization::RmcSerialize,
},
structures::RmcSerialize,
},
rnex_proxy_common::ConnectionInitData,
util::{SendingBufferConnection, SplittableBufferConnection},
}; };
use rnex_server::{ConnectionInitData, try_get_ip};
use rnex_util::{PID, SendingBufferConnection, SplittableBufferConnection, UnitPacketWrite};
use std::{ use std::{
env::{self, VarError}, env::{self, VarError},
fmt::Debug,
net::{AddrParseError, Ipv4Addr, SocketAddr, SocketAddrV4}, net::{AddrParseError, Ipv4Addr, SocketAddr, SocketAddrV4},
ops::Deref, ops::Deref,
panic, panic,
@ -24,6 +17,7 @@ use std::{
}; };
use thiserror::Error; use thiserror::Error;
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tracing::{error, info, instrument};
const RNEX_DEFAULT_PORT: u16 = match u16::from_str_radix(env!("RNEX_DEFAULT_PORT"), 10) { const RNEX_DEFAULT_PORT: u16 = match u16::from_str_radix(env!("RNEX_DEFAULT_PORT"), 10) {
Ok(v) => v, Ok(v) => v,
@ -104,6 +98,15 @@ impl ProxyStartupParam {
} }
struct OnRemoteDrop<T: RemoteDisconnectable, C: FnOnce() + Send + Sync + 'static>(T, Option<C>); struct OnRemoteDrop<T: RemoteDisconnectable, C: FnOnce() + Send + Sync + 'static>(T, Option<C>);
impl<T: RemoteDisconnectable + Debug, C: FnOnce() + Send + Sync + 'static> Debug
for OnRemoteDrop<T, C>
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut tuple_builder = f.debug_tuple("OnRemoteDrop");
tuple_builder.field(&self.0);
tuple_builder.finish_non_exhaustive()
}
}
impl<T: RemoteDisconnectable, C: FnOnce() + Send + Sync + 'static> Deref for OnRemoteDrop<T, C> { impl<T: RemoteDisconnectable, C: FnOnce() + Send + Sync + 'static> Deref for OnRemoteDrop<T, C> {
type Target = T; type Target = T;
@ -136,10 +139,10 @@ impl<T: RemoteDisconnectable, C: FnOnce() + Send + Sync + 'static> RmcCallable
_protocol_id: u16, _protocol_id: u16,
_method_id: u32, _method_id: u32,
_call_id: u32, _call_id: u32,
_rest: Vec<u8>, _rest: &[u8],
) -> impl Future<Output = ()> + Send { ) -> impl Future<Output = bool> + Send {
// maybe respond with not implemented or something // maybe respond with not implemented or something
async {} async { false }
} }
} }
@ -160,7 +163,7 @@ pub async fn setup_edge_node_connection(
let conn: SplittableBufferConnection = conn.into(); let conn: SplittableBufferConnection = conn.into();
conn.send( conn.send(
rnex_core::reggie::EdgeNodeHolderConnectOption::Register(param.self_public) EdgeNodeHolderConnectOption::Register(param.self_public)
.to_data() .to_data()
.unwrap(), .unwrap(),
) )
@ -168,7 +171,7 @@ pub async fn setup_edge_node_connection(
println!("{:?}", param.self_public); println!("{:?}", param.self_public);
//leave the inner object floating so that it gets destroyed once we disconnect //leave the inner object floating so that it gets destroyed once we disconnect
new_rmc_gateway_connection(conn, move |r| { new_rmc_gateway_connection(conn, async move |r| {
Arc::new(OnRemoteDrop::<RemoteEdgeNodeHolder, _>::new( Arc::new(OnRemoteDrop::<RemoteEdgeNodeHolder, _>::new(
r, r,
shutdown_callback, shutdown_callback,
@ -191,7 +194,7 @@ pub async fn new_backend_connection(
}; };
let data = ConnectionInitData { let data = ConnectionInitData {
prudpsock_addr: addr, addr: addr.regular_socket_addr,
pid: pid, pid: pid,
} }
.to_data() .to_data()

View file

@ -3,20 +3,20 @@ name = "proxy"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
[lints]
workspace = true
[dependencies] [dependencies]
tokio = { version = "1.47.0", features = ["full"] } tokio = { version = "1.47.0", features = ["full"] }
prudpv0 = { path = "../prudpv0", optional = true } prudpv0 = { path = "../prudpv0", optional = true }
prudpv1 = { path = "../prudpv1", optional = true } prudpv1 = { path = "../prudpv1", optional = true }
prudplite = { path = "../prudplite", optional = true }
proxy-common = { path = "../proxy-common" } proxy-common = { path = "../proxy-common" }
cfg-if = "1.0.4" cfg-if = "1.0.4"
rnex-core = { path = "../rnex-core", version = "0.1.1" } rnex-prudp = { path = "../rnex-prudp" }
log = "0.4.25"
[features] [features]
prudpv0 = ["dep:prudpv0"] prudpv0 = ["dep:prudpv0"]
prudpv1 = ["dep:prudpv1"] prudpv1 = ["dep:prudpv1"]
prudplite = ["dep:prudplite"]
friends = ["prudpv0", "prudpv0/friends"] friends = ["prudpv0", "prudpv0/friends"]
splatoon = ["prudpv1"] splatoon = ["prudpv1"]

View file

@ -1,18 +0,0 @@
[package]
name = "prudplite"
version = "0.1.0"
edition = "2024"
[features]
nx = []
v4-3-11 = []
[dependencies]
rnex-core = { path = "../rnex-core", version = "0.1.1" }
tokio = { version = "1.47.0", features = ["full"] }
bytemuck = { version = "1.23.1", features = ["derive"] }
proxy-common = {path = "../proxy-common"}
tokio-tungstenite = {version = "0.29.0", features = ["rustls", "rustls-tls-native-roots"]}
log = "0.4.25"
futures-util = "0.3.31"
v-byte-helpers = { git = "https://github.com/RusticMaple/VByteMacros", version = "0.1.1" }

View file

@ -1,14 +0,0 @@
use rnex_core::PID;
use crate::crypto::Crypto;
pub struct Insecure;
impl Crypto for Insecure {
fn new_connection(&self, _data: &[u8]) -> Option<(PID, Vec<u8>)> {
Some((100, vec![]))
}
fn new() -> Self {
Self
}
}

View file

@ -1,9 +0,0 @@
use rnex_core::PID;
pub mod insecure;
pub mod secure;
pub trait Crypto: 'static + Send + Sync {
fn new_connection(&self, data: &[u8]) -> Option<(PID, Vec<u8>)>;
fn new() -> Self;
}

View file

@ -1,27 +0,0 @@
use rnex_core::{
PID, executables::common::SECURE_SERVER_ACCOUNT, nex::account::Account,
prudp::ticket::read_secure_connection_data, rmc::structures::RmcSerialize,
};
use crate::crypto::Crypto;
pub struct Secure(&'static Account);
impl Crypto for Secure {
fn new_connection(&self, data: &[u8]) -> Option<(PID, Vec<u8>)> {
let (_, pid, check_value) = read_secure_connection_data(data, &self.0)?;
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()?;
Some((pid, response))
}
fn new() -> Self {
Self(&SECURE_SERVER_ACCOUNT)
}
}

View file

@ -1,45 +0,0 @@
use futures_util::{SinkExt, StreamExt};
use rnex_core::prudp::types_flags::{TypesFlags, flags::NEED_ACK, types::SYN};
use tokio_tungstenite::tungstenite::{Message, client::IntoClientRequest, http::header};
use crate::packet::{LiteHeader, LitePacket, PacketSpecificData, StreamTypes, create_packet_from};
mod packet;
const KEY: &str = "4eb18d39";
const URL: &str = "wss://g2DF33D01-lp1.s.n.srv.nintendo.net";
#[tokio::main]
async fn main() {
let login = URL.into_client_request().unwrap();
let (mut stream, response) = tokio_tungstenite::connect_async(login).await.unwrap();
println!("response: {:?}", response);
let packet = create_packet_from(
LiteHeader {
stream_types: StreamTypes::new(10, 10),
source_port: 1,
destination_port: 1,
fragment_id: 0,
types_flags: TypesFlags::default().types(SYN).flags(NEED_ACK),
sequence_id: 0,
..Default::default()
},
&[PacketSpecificData::SupportedFunctions(0x8)],
&[],
);
println!("sending ack");
stream.send(Message::Binary(packet.into())).await.unwrap();
println!("waiting for response");
let packet = stream.next().await.unwrap();
let Message::Binary(packet) = packet.unwrap() else {
panic!()
};
let packet = LitePacket::new(packet);
let header = packet.header().unwrap();
println!("{:?}", header);
}

View file

@ -1,316 +0,0 @@
pub mod crypto;
mod packet;
use std::{collections::HashMap, net::SocketAddr, sync::Arc};
use crate::{
crypto::{Crypto, insecure::Insecure, secure::Secure},
packet::{LiteHeader, LitePacket, PacketSpecificData, StreamTypes, create_packet_from},
};
use futures_util::{SinkExt, StreamExt};
use log::{error, info, warn};
use proxy_common::{ProxyStartupParam, new_backend_connection};
use rnex_core::{
PID,
prudp::{
socket_addr::PRUDPSockAddr,
types_flags::{
TypesFlags,
flags::{ACK, NEED_ACK, RELIABLE},
types::{CONNECT, DATA, DISCONNECT, PING, SYN},
},
virtual_port::VirtualPort,
},
util::SplittableBufferConnection,
};
use tokio::net::{TcpListener, TcpStream};
use tokio_tungstenite::{
WebSocketStream,
tungstenite::{Bytes, Message},
};
struct ConnectionState {
param: Arc<ProxyStartupParam>,
active: bool,
websocket: WebSocketStream<TcpStream>,
#[allow(dead_code)]
pid: PID,
backend_conn: SplittableBufferConnection,
addr: PRUDPSockAddr,
incoming_reliable: HashMap<u16, LitePacket<Bytes>>,
client_reliable_counter: u16,
#[allow(dead_code)]
server_reliable_counter: u16,
}
impl ConnectionState {
pub async fn handle_incoming_prudp(&mut self, packet: LitePacket<Bytes>, sorted: bool) {
let Some(header) = packet.header() else {
warn!("invalid data on connection");
return;
};
if (header.types_flags.get_flags() & NEED_ACK) != 0 {
let data = create_packet_from(
LiteHeader {
stream_types: StreamTypes::new(
self.param.virtual_port.get_stream_type(),
self.addr.virtual_port.get_stream_type(),
),
source_port: self.param.virtual_port.get_port_number(),
destination_port: self.addr.virtual_port.get_port_number(),
fragment_id: header.fragment_id,
types_flags: TypesFlags::default()
.types(header.types_flags.get_types())
.flags(ACK),
sequence_id: header.sequence_id,
..Default::default()
},
&[],
&[],
);
let data: Bytes = data.into();
if header.types_flags.get_types() == DISCONNECT {
self.websocket
.send(Message::Binary(data.clone()))
.await
.ok();
self.websocket
.send(Message::Binary(data.clone()))
.await
.ok();
}
self.websocket.send(Message::Binary(data)).await.ok();
}
if (header.types_flags.get_flags() & ACK) != 0 {
// we can just safely ignore acks, we ARE sending over tcp after all already guarantees that our packets will arrive
// we can however not guarantee the order of incoming client packets so we should still take care of that
// (the client might be doing some funny things which we dont know of)
return;
}
if (header.types_flags.get_flags() & RELIABLE != 0) & !sorted {
self.incoming_reliable.insert(header.sequence_id, packet);
if self.incoming_reliable.len() > 5 {
self.active = false;
warn!("client is spamming out of order reliable packets, throwing out");
}
return;
}
match header.types_flags.get_types() {
DATA => {
if header.fragment_id != 0 {
warn!("fragmented packets arent yet supported");
return;
}
let Some(payload) = packet.payload() else {
return;
};
self.backend_conn.send(payload.into()).await;
}
PING => {}
v => {
info!("unimplemented packet type: {}", v);
}
}
}
#[allow(dead_code)]
pub async fn process_reliable(&mut self) {
while let Some(v) = self.incoming_reliable.remove(&self.client_reliable_counter) {
self.handle_incoming_prudp(v, true).await;
self.client_reliable_counter += 1;
}
}
pub async fn handle_connection(&mut self) {
while self.active {
tokio::select! {
v = self.websocket.next() => {
match v {
Some(Ok(Message::Binary(v))) => {
self.handle_incoming_prudp(LitePacket::new(v), false).await;
}
_ => {
info!("client disconnected or errored out");
return;
}
}
}
_ = self.backend_conn.recv() => {
}
}
}
}
}
pub async fn websocket_thread_unconnected<C: Crypto>(
param: Arc<ProxyStartupParam>,
crypto: Arc<C>,
conn: TcpStream,
addr: SocketAddr,
) {
let mut websocket = match tokio_tungstenite::accept_async(conn).await {
Ok(v) => v,
Err(e) => {
error!("error accepting websocket connection: {}", e);
return;
}
};
while let Some(Ok(v)) = websocket.next().await {
match v {
Message::Binary(b) => {
let packet = LitePacket::new(b);
let Some(header) = packet.header() else {
error!("got malformed message, disconnecting");
return;
};
match header.types_flags.get_types() {
SYN => {
let Some(supported) = packet.packet_specific_iter() else {
error!("got malformed message, disconnecting");
return;
};
let Some(PacketSpecificData::SupportedFunctions(s)) = supported
.into_iter()
.find(|v| matches!(v, PacketSpecificData::SupportedFunctions(_)))
else {
error!("got malformed message, disconnecting");
return;
};
let data = create_packet_from(
LiteHeader {
destination_port: header.source_port,
source_port: param.virtual_port.get_port_number(),
stream_types: StreamTypes::new(
param.virtual_port.get_stream_type(),
header.stream_types.source(),
),
fragment_id: 0,
sequence_id: 0,
types_flags: TypesFlags::default().types(SYN).flags(ACK),
..Default::default()
},
&[
PacketSpecificData::SupportedFunctions(s & 0xFF),
PacketSpecificData::ConnectionSignature([0; 16]),
],
&[],
);
websocket.send(Message::Binary(data.into())).await.ok();
}
CONNECT => {
let Some(supported) = packet.packet_specific_iter() else {
error!("got malformed message, disconnecting");
return;
};
let Some(PacketSpecificData::SupportedFunctions(s)) = supported
.into_iter()
.find(|v| matches!(v, PacketSpecificData::SupportedFunctions(_)))
else {
error!("got malformed message, disconnecting");
return;
};
let Some(data) = packet.payload() else {
error!("got malformed message, disconnecting");
return;
};
let Some((pid, data)) = crypto.new_connection(data) else {
error!("invalid login data");
return;
};
let data = create_packet_from(
LiteHeader {
destination_port: header.source_port,
source_port: param.virtual_port.get_port_number(),
stream_types: StreamTypes::new(
param.virtual_port.get_stream_type(),
header.stream_types.source(),
),
fragment_id: 0,
sequence_id: 0,
types_flags: TypesFlags::default().types(CONNECT).flags(ACK),
..Default::default()
},
&[
PacketSpecificData::SupportedFunctions(s & 0xFF),
PacketSpecificData::ConnectionSignature([0; 16]),
],
&data,
);
websocket.send(Message::Binary(data.into())).await.ok();
let addr = PRUDPSockAddr::new(
addr,
VirtualPort::new(header.source_port, header.stream_types.source()),
);
let Some(backend_conn) = new_backend_connection(&param, addr, pid).await
else {
error!("unable to connect to backend");
return;
};
let mut connection = ConnectionState {
active: true,
addr,
pid,
backend_conn,
client_reliable_counter: 2,
server_reliable_counter: 1,
param,
incoming_reliable: HashMap::new(),
websocket,
};
connection.handle_connection().await;
break;
}
v => {
error!(
"invalid packet type for unconnected client {}, disconnecting",
v,
);
}
}
}
v => {
error!("non binary message({:?}) , disconnecting", v);
return;
}
}
}
}
pub async fn start_proxy<C: Crypto>(param: ProxyStartupParam) {
let param = Arc::new(param);
let crypto = Arc::new(C::new());
let listener = TcpListener::bind(param.self_private)
.await
.expect("unable to bind to port");
while let Ok((connection, addr)) = listener.accept().await {
let param = param.clone();
let crypto = crypto.clone();
tokio::spawn(websocket_thread_unconnected(
param, crypto, connection, addr,
));
}
}
pub async fn start_secure(param: ProxyStartupParam) {
start_proxy::<Secure>(param).await;
}
pub async fn start_insecure(param: ProxyStartupParam) {
start_proxy::<Insecure>(param).await;
}

View file

@ -1,222 +0,0 @@
use std::{
fmt::Debug,
io::{self, Cursor, Read, Write},
};
use bytemuck::{Pod, Zeroable, bytes_of_mut};
use rnex_core::prudp::types_flags::TypesFlags;
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
#[derive(Pod, Zeroable, Copy, Clone, Default, Debug)]
#[repr(C)]
pub struct LiteHeader {
pub magic: u8,
pub packet_specific_length: u8,
pub payload_size: u16,
pub stream_types: StreamTypes,
pub source_port: u8,
pub destination_port: u8,
pub fragment_id: u8,
pub types_flags: TypesFlags,
pub sequence_id: u16,
}
pub enum PacketSpecificData {
SupportedFunctions(u32),
ConnectionSignature([u8; 16]),
LiteSignature([u8; 16]),
}
impl PacketSpecificData {
fn consume(reader: &mut impl Read) -> io::Result<Self> {
let mut option_id = 0u8;
reader.read_exact(bytes_of_mut(&mut option_id))?;
let mut size = 0u8;
reader.read_exact(bytes_of_mut(&mut size))?;
match option_id {
0 => {
if size != 4 {
Err(io::Error::other(
"invalid option size for supported functions",
))
} else {
Ok(Self::SupportedFunctions(reader.read_le_u32()?))
}
}
1 => {
if size != 16 {
Err(io::Error::other(
"invalid option size for connection signature",
))
} else {
Ok(Self::ConnectionSignature(
reader.read_struct(IS_BIG_ENDIAN)?,
))
}
}
0x80 => {
if size != 16 {
Err(io::Error::other("invalid option size for lite signature"))
} else {
Ok(Self::LiteSignature(reader.read_struct(IS_BIG_ENDIAN)?))
}
}
_ => Err(io::Error::other("invalid option id")),
}
}
fn write_size(&self) -> usize {
2 + match self {
PacketSpecificData::SupportedFunctions(_) => 4,
Self::ConnectionSignature(_) => 16,
Self::LiteSignature(_) => 16,
}
}
fn write_self(&self, writer: &mut impl Write) -> io::Result<()> {
match self {
PacketSpecificData::SupportedFunctions(v) => {
writer.write_all(&[0, 4])?;
writer.write_all(&v.to_le_bytes())?;
}
Self::ConnectionSignature(v) => {
writer.write_all(&[1, 16])?;
writer.write_all(&v[..])?;
}
Self::LiteSignature(v) => {
writer.write_all(&[0x80, 16])?;
writer.write_all(&v[..])?;
}
}
Ok(())
}
}
pub struct LitePacket<T: AsRef<[u8]>>(T);
pub struct PacketSpecificIter<'a>(Cursor<&'a [u8]>);
impl<'a> Iterator for PacketSpecificIter<'a> {
type Item = PacketSpecificData;
fn next(&mut self) -> Option<Self::Item> {
PacketSpecificData::consume(&mut self.0).ok()
}
}
impl<T: AsRef<[u8]>> LitePacket<T> {
pub fn new(inner: T) -> Self {
Self(inner)
}
pub fn header(&self) -> Option<&LiteHeader> {
bytemuck::try_from_bytes(self.0.as_ref().get(..size_of::<LiteHeader>())?).ok()
}
pub fn header_mut(&mut self) -> Option<&mut LiteHeader>
where
T: AsMut<[u8]>,
{
bytemuck::try_from_bytes_mut(self.0.as_mut().get_mut(..size_of::<LiteHeader>())?).ok()
}
pub fn payload(&self) -> Option<&[u8]> {
let header = self.header()?;
self.0
.as_ref()
.get(size_of::<LiteHeader>() + header.packet_specific_length as usize..)
}
pub fn payload_mut(&mut self) -> Option<&mut [u8]>
where
T: AsMut<[u8]>,
{
let len = self.header()?.packet_specific_length;
self.0
.as_mut()
.get_mut(size_of::<LiteHeader>() + len as usize..)
}
pub fn packet_specific_raw(&self) -> Option<&[u8]> {
let header = self.header()?;
self.0.as_ref().get(
size_of::<LiteHeader>()
..size_of::<LiteHeader>() + header.packet_specific_length as usize,
)
}
pub fn packet_specific_raw_mut(&mut self) -> Option<&mut [u8]>
where
T: AsMut<[u8]>,
{
let len = self.header()?.packet_specific_length;
self.0
.as_mut()
.get_mut(size_of::<LiteHeader>()..size_of::<LiteHeader>() + len as usize)
}
pub fn packet_specific_iter<'a>(&'a self) -> Option<PacketSpecificIter<'a>> {
self.packet_specific_raw()
.map(Cursor::new)
.map(PacketSpecificIter)
}
}
pub fn create_packet_from(
header: LiteHeader,
specific_data: &[PacketSpecificData],
data: &[u8],
) -> Vec<u8> {
let specific_size: usize = specific_data.iter().map(|v| v.write_size()).sum();
let mut packet = LitePacket::new(vec![
0u8;
size_of::<LiteHeader>() + specific_size + data.len()
]);
*packet.header_mut().expect("packet malformed in creation") = LiteHeader {
magic: 0x80,
packet_specific_length: specific_size as u8,
payload_size: data.len() as u16,
..header
};
let mut cursor = Cursor::new(
packet
.packet_specific_raw_mut()
.expect("packet malformed in creation"),
);
for specific in specific_data {
specific.write_self(&mut cursor).unwrap();
}
packet
.payload_mut()
.expect("packet malformed in creation")
.copy_from_slice(data);
packet.0
}
#[derive(Pod, Zeroable, Copy, Clone, Default)]
#[repr(transparent)]
pub struct StreamTypes(u8);
impl Debug for StreamTypes {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({},{})", self.source(), self.destination())
}
}
impl StreamTypes {
pub fn new(source_stream: u8, dest_stream: u8) -> Self {
Self((source_stream & 0xF << 4) & dest_stream & 0xF)
}
pub fn source(&self) -> u8 {
self.0 >> 4
}
pub fn destination(&self) -> u8 {
self.0 & 0xF
}
}

View file

@ -3,13 +3,15 @@ name = "prudpv0"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
[lints]
workspace = true
[dependencies] [dependencies]
rnex-core = { path = "../rnex-core", version = "0.1.1" } rnex-prudp = { path = "../rnex-prudp" }
tokio = { version = "1.47.0", features = ["full"] } tokio = { version = "1.47.0", features = ["full"] }
bytemuck = { version = "1.23.1", features = ["derive"] } bytemuck = { version = "1.23.1", features = ["derive"] }
typenum = "1.18.0" typenum = "1.18.0"
rc4 = "0.1.0" rc4 = "0.1.0"
log = "0.4.25"
cfg-if = "1.0.4" cfg-if = "1.0.4"
proxy-common = {path = "../proxy-common"} proxy-common = {path = "../proxy-common"}
hmac = "0.12.1" hmac = "0.12.1"

View file

@ -3,7 +3,11 @@ name = "prudpv1"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
[lints]
workspace = true
[dependencies] [dependencies]
bytemuck = { version = "1.23.1", features = ["derive"] } bytemuck = { version = "1.23.1", features = ["derive"] }
tokio = { version = "1.47.0", features = ["full"] } tokio = { version = "1.47.0", features = ["full"] }
hmac = "0.12.1" hmac = "0.12.1"
@ -11,11 +15,11 @@ md-5 = "^0.10.6"
rc4 = "0.1.0" rc4 = "0.1.0"
v-byte-helpers = { git = "https://github.com/RusticMaple/VByteMacros", version = "0.1.1" } v-byte-helpers = { git = "https://github.com/RusticMaple/VByteMacros", version = "0.1.1" }
thiserror = "2.0.12" thiserror = "2.0.12"
log = "0.4.27"
async-trait = "0.1.88" async-trait = "0.1.88"
typenum = "1.18.0" typenum = "1.18.0"
once_cell = "1.21.3" once_cell = "1.21.3"
rnex-core = { path = "../rnex-core", version = "0.1.1" } rnex-prudp = { path = "../rnex-prudp" }
rnex-util = { path = "../rnex-util" }
proxy-common = {path = "../proxy-common"} proxy-common = {path = "../proxy-common"}
cfg-if = "1.0.4" cfg-if = "1.0.4"

View file

@ -2,7 +2,6 @@ use crate::prudp::packet::PRUDPV1Packet;
use crate::prudp::router::Error::VirtualPortTaken; use crate::prudp::router::Error::VirtualPortTaken;
use crate::prudp::socket::{AnyInternalSocket, CryptoHandler, ExternalSocket, new_socket_pair}; use crate::prudp::socket::{AnyInternalSocket, CryptoHandler, ExternalSocket, new_socket_pair};
use log::{error, info}; use log::{error, info};
use rnex_core::prudp::virtual_port::VirtualPort;
use std::io; use std::io;
use std::io::Cursor; use std::io::Cursor;
use std::marker::PhantomData; use std::marker::PhantomData;

View file

@ -3,11 +3,6 @@ use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance};
use hmac::digest::consts::U32; use hmac::digest::consts::U32;
use rc4::cipher::StreamCipherCoreWrapper; use rc4::cipher::StreamCipherCoreWrapper;
use rc4::{KeyInit, Rc4, Rc4Core, StreamCipher}; use rc4::{KeyInit, Rc4, Rc4Core, StreamCipher};
use rnex_core::PID;
use rnex_core::nex::account::Account;
use rnex_core::prudp::encryption::EncryptionPair;
use rnex_core::prudp::ticket::read_secure_connection_data;
use rnex_core::rmc::structures::RmcSerialize;
use typenum::U5; use typenum::U5;
type Rc4U32 = StreamCipherCoreWrapper<Rc4Core<U32>>; type Rc4U32 = StreamCipherCoreWrapper<Rc4Core<U32>>;

View file

@ -6,12 +6,6 @@ use async_trait::async_trait;
use log::error; use log::error;
use log::{info, warn}; use log::{info, warn};
use rc4::StreamCipher; use rc4::StreamCipher;
use rnex_core::PID;
use rnex_core::prudp::socket_addr::PRUDPSockAddr;
use rnex_core::prudp::types_flags::TypesFlags;
use rnex_core::prudp::types_flags::flags::{ACK, HAS_SIZE, MULTI_ACK, NEED_ACK, RELIABLE};
use rnex_core::prudp::types_flags::types::{CONNECT, DATA, DISCONNECT, PING, SYN};
use rnex_core::prudp::virtual_port::VirtualPort;
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use std::io::Cursor; use std::io::Cursor;
use std::marker::PhantomData; use std::marker::PhantomData;

View file

@ -1,14 +1,14 @@
use crate::prudp::packet::PRUDPV1Packet; use crate::prudp::packet::PRUDPV1Packet;
use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance}; use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance};
use rc4::{KeyInit, Rc4, StreamCipher}; use rc4::{KeyInit, Rc4, StreamCipher};
use rnex_core::prudp::encryption::{DEFAULT_KEY, EncryptionPair}; use rnex_prudp::encryption::{DEFAULT_KEY, EncryptionPair};
use typenum::U5; use typenum::U5;
pub struct Unsecure(pub &'static str); pub struct Unsecure(pub &'static str);
pub struct UnsecureInstance { pub struct UnsecureInstance {
key: &'static str, key: &'static str,
streams: Vec<EncryptionPair<Rc4<U5>>>, streams: Vec<EncryptionPair<Rc4>>,
self_signature: [u8; 16], self_signature: [u8; 16],
#[allow(dead_code)] #[allow(dead_code)]
remote_signature: [u8; 16], remote_signature: [u8; 16],
@ -32,7 +32,11 @@ impl CryptoHandler for Unsecure {
Vec::new(), Vec::new(),
UnsecureInstance { UnsecureInstance {
streams: (0..substream_count) streams: (0..substream_count)
.map(|_| EncryptionPair::init_both(|| Rc4::new(&DEFAULT_KEY))) .map(|_| {
EncryptionPair::init_both(|| {
Rc4::new_from_slice(DEFAULT_KEY).expect("invalid key length")
})
})
.collect(), .collect(),
key: self.0, key: self.0,
remote_signature, remote_signature,

View file

@ -3,16 +3,16 @@ name = "rnex-core"
version = "0.1.1" version = "0.1.1"
edition = "2024" edition = "2024"
[lints]
workspace = true
[dependencies] [dependencies]
bytemuck = { version = "1.21.0", features = ["derive"] } bytemuck = { version = "1.21.0", features = ["derive"] }
dotenv = "0.15.0" dotenv = "0.15.0"
once_cell = "1.20.2"
rc4 = "0.1.0" rc4 = "0.1.0"
thiserror = "2.0.11" thiserror = "2.0.11"
v-byte-helpers = { git = "https://github.com/RusticMaple/VByteMacros", version = "0.1.1" } v-byte-helpers = { git = "https://github.com/RusticMaple/VByteMacros", version = "0.1.1" }
simplelog = "0.12.2"
chrono = "0.4.39" chrono = "0.4.39"
log = "0.4.25"
rand = "0.10.0" rand = "0.10.0"
cfg-if = "1.0.4" cfg-if = "1.0.4"
hmac = "0.12.1" hmac = "0.12.1"
@ -20,7 +20,7 @@ md-5 = "^0.10.6"
tokio = { version = "1.43.0", features = ["full"] } tokio = { version = "1.43.0", features = ["full"] }
hex = "0.4.3" hex = "0.4.3"
macros = { path = "../macros" } rnex-rmc = { path = "../rnex-rmc" }
paste = "1.0.15" paste = "1.0.15"
typenum = "1.18.0" typenum = "1.18.0"
json = "0.12.4" json = "0.12.4"
@ -39,7 +39,7 @@ async-trait = "0.1.89"
ctor = "1.0.7" ctor = "1.0.7"
nex-account = { version = "0.2.1", registry = "spbr" } nex-account = { version = "0.2.1", registry = "spbr" }
tonic = "0.14.6" tonic = "0.14.6"
tracing = { version = "0.1.44", features = ["log"] } tracing = { version = "0.1.44" }
tracing-subscriber = "0.3.23" tracing-subscriber = "0.3.23"
sentry-tracing = "0.48.4" sentry-tracing = "0.48.4"
sentry = { version = "0.48.4", features = ["tracing"] } sentry = { version = "0.48.4", features = ["tracing"] }
@ -52,8 +52,8 @@ rmc_struct_header = []
guest_login = [] guest_login = []
friends = ["guest_login", "database-support"] friends = ["guest_login", "database-support"]
big_pid = [] big_pid = []
v3-3-2 = []
third-notif-param = [] third-notif-param = []
v3-3-2 = []
v3-4-0 = ["v3-3-2", "third-notif-param", "rmc_struct_header"] v3-4-0 = ["v3-3-2", "third-notif-param", "rmc_struct_header"]
v3-5-0 = ["v3-4-0"] v3-5-0 = ["v3-4-0"]
v3-8-15 = ["v3-5-0"] v3-8-15 = ["v3-5-0"]

View file

@ -1,7 +1,5 @@
use crate::reggie::UnitPacketRead; use crate::reggie::UnitPacketRead;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use log::error;
use once_cell::sync::Lazy;
use rnex_core::nex::account::Account; use rnex_core::nex::account::Account;
use rnex_core::rmc::protocols::{RmcCallable, RmcConnection, new_rmc_gateway_connection}; use rnex_core::rmc::protocols::{RmcCallable, RmcConnection, new_rmc_gateway_connection};
use rnex_core::rmc::structures::RmcSerialize; use rnex_core::rmc::structures::RmcSerialize;
@ -13,6 +11,7 @@ use std::io::{Cursor, Read, Write};
use std::net::{Ipv4Addr, SocketAddrV4, TcpStream}; use std::net::{Ipv4Addr, SocketAddrV4, TcpStream};
use std::sync::Arc; use std::sync::Arc;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tracing::error;
const IP_REQ_SERVICE_URLS: &[(&str, &str, &str)] = &[ const IP_REQ_SERVICE_URLS: &[(&str, &str, &str)] = &[
("ipinfo.io:80", "ipinfo.io", "/ip"), ("ipinfo.io:80", "ipinfo.io", "/ip"),
@ -70,12 +69,12 @@ pub fn try_get_ip() -> Option<Ipv4Addr> {
let mut stream = TcpStream::connect(url.0)?; let mut stream = TcpStream::connect(url.0)?;
stream.write_all( stream.write_all(
format!( format!(
r#"GET {} HTTP/1.0 "GET {} HTTP/1.0
Host: {} Host: {}
User-Agent: RNEX User-Agent: RNEX
Accept: */* Accept: */*
"#, ",
url.2, url.1 url.2, url.1
) )
.as_str() .as_str()
@ -95,37 +94,37 @@ Accept: */*
None None
} }
pub static OWN_IP_PRIVATE: Lazy<Ipv4Addr> = Lazy::new(|| { pub static OWN_IP_PRIVATE: LazyLock<Ipv4Addr> = LazyLock::new(|| {
env::var("SERVER_IP") env::var("SERVER_IP")
.ok() .ok()
.map(|s| s.parse().expect("invalid ip address")) .map(|s| s.parse().expect("invalid ip address"))
.unwrap_or(Ipv4Addr::UNSPECIFIED) .unwrap_or(Ipv4Addr::UNSPECIFIED)
}); });
pub static OWN_IP_PUBLIC: Lazy<Ipv4Addr> = Lazy::new(|| { pub static OWN_IP_PUBLIC: LazyLock<Ipv4Addr> = LazyLock::new(|| {
env::var("SERVER_IP_PUBLIC") env::var("SERVER_IP_PUBLIC")
.ok() .ok()
.map(|s| s.parse().expect("invalid ip address")) .map(|s| s.parse().expect("invalid ip address"))
.unwrap_or_else(|| try_get_ip().unwrap()) .unwrap_or_else(|| try_get_ip().unwrap())
}); });
pub static SERVER_PORT: Lazy<u16> = Lazy::new(|| { pub static SERVER_PORT: LazyLock<u16> = LazyLock::new(|| {
env::var("SERVER_PORT") env::var("SERVER_PORT")
.ok() .ok()
.and_then(|s| s.parse().ok()) .and_then(|s| s.parse().ok())
.unwrap_or(10000) .unwrap_or(10000)
}); });
pub static KERBEROS_SERVER_PASSWORD: Lazy<String> = Lazy::new(|| { pub static KERBEROS_SERVER_PASSWORD: LazyLock<String> = LazyLock::new(|| {
env::var("AUTH_SERVER_PASSWORD") env::var("AUTH_SERVER_PASSWORD")
.ok() .ok()
.unwrap_or("password".to_owned()) .unwrap_or("password".to_owned())
}); });
pub static AUTH_SERVER_ACCOUNT: Lazy<Account> = pub static AUTH_SERVER_ACCOUNT: LazyLock<Account> =
Lazy::new(|| Account::new(1, "Quazal Authentication", &KERBEROS_SERVER_PASSWORD)); LazyLock::new(|| Account::new(1, "Quazal Authentication", &KERBEROS_SERVER_PASSWORD));
pub static SECURE_SERVER_ACCOUNT: Lazy<Account> = pub static SECURE_SERVER_ACCOUNT: LazyLock<Account> =
Lazy::new(|| Account::new(2, "Quazal Rendez-Vous", &KERBEROS_SERVER_PASSWORD)); LazyLock::new(|| Account::new(2, "Quazal Rendez-Vous", &KERBEROS_SERVER_PASSWORD));
pub async fn new_simple_backend<T: RmcCallable + Sync + Send + 'static, F>(mut creation_function: F) pub async fn new_simple_backend<T: RmcCallable + Sync + Send + 'static, F>(mut creation_function: F)
where where

View file

@ -4,8 +4,8 @@ use std::{
sync::{Arc, atomic::AtomicU32}, sync::{Arc, atomic::AtomicU32},
}; };
use log::error;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tracing::error;
use crate::{ use crate::{
executables::common::{OWN_IP_PRIVATE, SERVER_PORT}, executables::common::{OWN_IP_PRIVATE, SERVER_PORT},

View file

@ -11,8 +11,6 @@ pub type PID = i32;
pub use ctor::ctor; pub use ctor::ctor;
extern crate self as rnex_core;
pub mod prudp; pub mod prudp;
pub mod rmc; pub mod rmc;
//mod protocols; //mod protocols;
@ -23,12 +21,6 @@ pub mod grpc;
pub mod kerberos; pub mod kerberos;
pub mod nex; pub mod nex;
pub mod reggie; pub mod reggie;
pub mod result;
pub mod rnex_proxy_common; pub mod rnex_proxy_common;
pub mod util; pub mod util;
pub mod versions; pub mod versions;
pub use macros::*;
pub mod config {
pub const FEATURE_HAS_STRUCT_HEADER: bool = cfg!(feature = "rmc_struct_header");
}

View file

@ -23,11 +23,11 @@ pub async fn get_station_urls(
for station in station_urls { for station in station_urls {
let is_public = station.options.iter().any(|v| { let is_public = station.options.iter().any(|v| {
if let NatType(v) = v { if let NatType(v) = v
if *v & PUBLIC != 0 { && *v & PUBLIC != 0
{
return true; return true;
} }
}
false false
}); });
@ -63,14 +63,16 @@ pub async fn get_station_urls(
} else { } else {
let mut public_station = private_station.clone(); let mut public_station = private_station.clone();
public_station.options.retain(|v| match v { public_station.options.retain(|v| {
Address(_) | Port(_) | NatFiltering(_) | NatMapping(_) | NatType(_) => false, !matches!(
_ => true, v,
Address(_) | Port(_) | NatFiltering(_) | NatMapping(_) | NatType(_)
)
}); });
public_station public_station
.options .options
.push(Address(addr.regular_socket_addr.ip().clone())); .push(Address(addr.regular_socket_addr.ip()));
public_station public_station
.options .options
.push(Port(addr.regular_socket_addr.port())); .push(Port(addr.regular_socket_addr.port()));
@ -84,10 +86,9 @@ pub async fn get_station_urls(
let both = [&mut public_station, &mut private_station]; let both = [&mut public_station, &mut private_station];
for station in both { for station in both {
station.options.retain(|v| match v { station
PrincipalID(_) | RVConnectionID(_) => false, .options
_ => true, .retain(|v| !matches!(v, PrincipalID(_) | RVConnectionID(_)));
});
station.options.push(PrincipalID(pid)); station.options.push(PrincipalID(pid));
station.options.push(RVConnectionID(cid)); station.options.push(RVConnectionID(cid));
@ -96,3 +97,7 @@ pub async fn get_station_urls(
Ok(vec![public_station]) Ok(vec![public_station])
} }
#[track_caller]
#[cfg(feature = "database-support")]
fn test() {}

View file

@ -1,5 +1,5 @@
use std::convert; use crate::rmc::protocols::datastore::DataStoreGetCustomRankingParam;
use chrono::{NaiveDateTime, Utc}; use chrono::Utc;
use futures::TryStreamExt; use futures::TryStreamExt;
use futures::future::join_all; use futures::future::join_all;
use rnex_core::PID; use rnex_core::PID;
@ -15,16 +15,17 @@ use rnex_core::rmc::protocols::datastore::{
}; };
use rnex_core::rmc::protocols::datastore::{ use rnex_core::rmc::protocols::datastore::{
BufferQueueParam, CompletePostParam, DataStoreChangeMetaParam, DataStoreCustomRankingResult, BufferQueueParam, CompletePostParam, DataStoreChangeMetaParam, DataStoreCustomRankingResult,
DataStoreGetCustomRankingByDataIDParam, DataStorePrepareGetParam, DataStoreReqGetInfo,
DataStoreSearchParam, GetMetaInfo, GetMetaParam, KeyValue, Permission, PersistenceTarget,
RateCustomRankingParam, RatingInfo, RatingInfoWithSlot, RatingInitParamWithSlot,
DataStoreDeleteParam, DataStoreFileServerObjectInfo, DataStoreGetCourseRecordParam, DataStoreDeleteParam, DataStoreFileServerObjectInfo, DataStoreGetCourseRecordParam,
DataStoreGetCourseRecordResult, DataStoreReportCourseParam, DataStoreUploadCourseRecordParam DataStoreGetCourseRecordResult, DataStoreGetCustomRankingByDataIDParam,
DataStorePrepareGetParam, DataStoreReportCourseParam, DataStoreReqGetInfo,
DataStoreSearchParam, DataStoreUploadCourseRecordParam, GetMetaInfo, GetMetaParam, KeyValue,
Permission, PersistenceTarget, RateCustomRankingParam, RatingInfo, RatingInfoWithSlot,
RatingInitParamWithSlot,
}; };
use rnex_core::rmc::response::ErrorCode; use rnex_core::rmc::response::ErrorCode;
use rnex_core::rmc::structures::qbuffer::QBuffer; use rnex_core::rmc::structures::qbuffer::QBuffer;
use rnex_core::rmc::structures::qresult::QResult; use rnex_core::rmc::structures::qresult::QResult;
use crate::rmc::protocols::datastore::DataStoreGetCustomRankingParam; use std::convert;
fn map_row_to_meta_info( fn map_row_to_meta_info(
row_data_id: i64, row_data_id: i64,
@ -324,7 +325,7 @@ fn filter_properties_by_result_option(meta_info: &mut GetMetaInfo, result_option
async fn init_object_rating_slot(data_id: i64, rating_param: RatingInitParamWithSlot) { async fn init_object_rating_slot(data_id: i64, rating_param: RatingInitParamWithSlot) {
log::info!("running init object rating slot"); log::info!("running init object rating slot");
let row = sqlx::query!( sqlx::query!(
r#" r#"
INSERT INTO datastore.object_ratings ( INSERT INTO datastore.object_ratings (
data_id, data_id,
@ -610,7 +611,7 @@ async fn rate_object(
let rating = RatingInfo::default(); let rating = RatingInfo::default();
let row = sqlx::query!( sqlx::query!(
r#" r#"
UPDATE datastore.object_ratings UPDATE datastore.object_ratings
SET total_value=total_value+$1, count=count+1 SET total_value=total_value+$1, count=count+1
@ -690,7 +691,7 @@ async fn get_rating_with_slot_data_id(dataid: i64) -> Result<Vec<RatingInfoWithS
pub async fn insert_buffer(dataid: i64, slot: i32, buffer: &QBuffer) { pub async fn insert_buffer(dataid: i64, slot: i32, buffer: &QBuffer) {
let db_now = Utc::now().naive_utc(); let db_now = Utc::now().naive_utc();
let row = sqlx::query!( sqlx::query!(
r#" r#"
INSERT INTO datastore.buffer_queues ( INSERT INTO datastore.buffer_queues (
data_id, data_id,
@ -914,52 +915,65 @@ impl DataStore for User {
let config = match appid { let config = match appid {
0 => vec![ 0 => vec![
0x00000001, 0x0000_0001,
0x00000032, 0x0000_0032,
0x00000096, 0x0000_0096,
0x0000012c, 0x0000_012c,
0x000001f4, 0x0000_01f4,
0x00000320, 0x0000_0320,
0x00000514, 0x0000_0514,
0x000007d0, 0x0000_07d0,
0x00000bb8, 0x0000_0bb8,
0x00001388, 0x0000_1388,
MAX_COURSE_UPLOADS, MAX_COURSE_UPLOADS,
0x00000014, 0x0000_0014,
0x0000001e, 0x0000_001e,
0x00000028, 0x0000_0028,
0x00000032, 0x0000_0032,
0x0000003c, 0x0000_003c,
0x00000046, 0x0000_0046,
0x00000050, 0x0000_0050,
0x0000005a, 0x0000_005a,
0x00000064, 0x0000_0064,
0x00000023, 0x0000_0023,
0x0000004b, 0x0000_004b,
0x00000023, 0x0000_0023,
0x0000004b, 0x0000_004b,
0x00000032, 0x0000_0032,
0x00000000, 0x0000_0000,
0x00000003, 0x0000_0003,
0x00000003, 0x0000_0003,
0x00000064, 0x0000_0064,
0x00000006, 0x0000_0006,
0x00000001, 0x0000_0001,
0x00000060, 0x0000_0060,
0x00000005, 0x0000_0005,
0x00000060, 0x0000_0060,
0x00000000, 0x0000_0000,
0x000007e4, 0x0000_07e4,
0x00000001, 0x0000_0001,
0x00000001, 0x0000_0001,
0x0000000c, 0x0000_000c,
0x00000000, 0x0000_0000,
], ],
1 => vec![ 1 => vec![
2, 1770179696, 1770179664, 1770179640, 1770180827, 1770180777, 1770180745, 2,
1770177625, 1770177590, 1_770_179_696,
1_770_179_664,
1_770_179_640,
1_770_180_827,
1_770_180_777,
1_770_180_745,
1_770_177_625,
1_770_177_590,
],
2 => vec![
0x0000_07df,
0x0000_000c,
0x0000_0016,
0x0000_0005,
0x0000_0000,
], ],
2 => vec![0x000007df, 0x0000000c, 0x00000016, 0x00000005, 0x00000000],
10 => vec![35, 75, 96, 40, 5, 6], 10 => vec![35, 75, 96, 40, 5, 6],
_ => { _ => {
log::error!("unknown SMM app id: {}", appid); log::error!("unknown SMM app id: {}", appid);
@ -1489,7 +1503,7 @@ impl DataStore for User {
.map(KerberosDateTime::from_naive) .map(KerberosDateTime::from_naive)
.unwrap_or_default(); .unwrap_or_default();
let mut meta_info = GetMetaInfo { let meta_info = GetMetaInfo {
dataid: row.data_id, dataid: row.data_id,
owner: row.owner.unwrap_or(0), owner: row.owner.unwrap_or(0),
size: row.size.unwrap_or(0) as u32, size: row.size.unwrap_or(0) as u32,
@ -1529,7 +1543,7 @@ impl DataStore for User {
) -> Result<(), ErrorCode> { ) -> Result<(), ErrorCode> {
let now = Utc::now().naive_utc(); let now = Utc::now().naive_utc();
let row = sqlx::query!( sqlx::query!(
r#" r#"
INSERT INTO datastore.course_records ( INSERT INTO datastore.course_records (
data_id, data_id,
@ -1667,12 +1681,18 @@ impl DataStore for User {
&self, &self,
application_id: u32, application_id: u32,
) -> Result<bool, ErrorCode> { ) -> Result<bool, ErrorCode> {
// official servers always return true? application ID is always 0 as far as i know. maybe a check is warranted for the app id? if application_id == 0 {
Ok(true) Ok(true)
} else {
Err(ErrorCode::Core_Unknown)
}
} }
async fn report_course(&self, report_course_param: DataStoreReportCourseParam) -> Result<(), ErrorCode> { async fn report_course(
let row = sqlx::query!( &self,
report_course_param: DataStoreReportCourseParam,
) -> Result<(), ErrorCode> {
sqlx::query!(
r#" r#"
INSERT INTO datastore.reports ( INSERT INTO datastore.reports (
data_id, data_id,
@ -1722,7 +1742,7 @@ impl DataStore for User {
log::info!("update password check passed"); log::info!("update password check passed");
let deletequery = sqlx::query!( sqlx::query!(
"UPDATE datastore.objects SET deleted=true WHERE data_id=$1", "UPDATE datastore.objects SET deleted=true WHERE data_id=$1",
param.dataid param.dataid
) )
@ -1738,7 +1758,7 @@ impl DataStore for User {
async fn get_custom_ranking( async fn get_custom_ranking(
&self, &self,
param: DataStoreGetCustomRankingParam param: DataStoreGetCustomRankingParam,
) -> Result<(Vec<DataStoreCustomRankingResult>, Vec<QResult>), ErrorCode> { ) -> Result<(Vec<DataStoreCustomRankingResult>, Vec<QResult>), ErrorCode> {
let mut ranking_results = Vec::new(); let mut ranking_results = Vec::new();
@ -1811,7 +1831,12 @@ impl DataStore for User {
Ok((ranking_results, q_results)) 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> { // todo: respect the search parameters and extra data
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 courses = Vec::new();
let mut stream = sqlx::query!( let mut stream = sqlx::query!(
@ -1885,7 +1910,7 @@ impl DataStore for User {
.map(KerberosDateTime::from_naive) .map(KerberosDateTime::from_naive)
.unwrap_or_default(); .unwrap_or_default();
let mut meta_info = GetMetaInfo { let meta_info = GetMetaInfo {
dataid: row.data_id, dataid: row.data_id,
owner: row.owner.unwrap_or(0), owner: row.owner.unwrap_or(0),
size: row.size.unwrap_or(0) as u32, size: row.size.unwrap_or(0) as u32,

View file

@ -4,26 +4,22 @@ use std::sync::{LazyLock, Weak};
use std::time::Duration; use std::time::Duration;
use std::{env, mem}; use std::{env, mem};
use bytemuck::{Pod, Zeroable}; use crate::rmc::protocols::account_management::{
use chrono::{TimeZone, Utc};
use log::info;
use macros::rmc_struct;
use rnex_core::rmc::protocols::account_management::{
AccountExtraInfo, AccountManagement, RawAccountManagement, RawAccountManagementInfo, AccountExtraInfo, AccountManagement, RawAccountManagement, RawAccountManagementInfo,
RemoteAccountManagement, RemoteAccountManagement,
}; };
use rnex_core::rmc::protocols::friends_3ds::{ use crate::rmc::protocols::friends_3ds::{
Friends3DS, RawFriends3DS, RawFriends3DSInfo, RemoteFriends3DS, Friends3DS, RawFriends3DS, RawFriends3DSInfo, RemoteFriends3DS,
}; };
use rnex_core::rmc::protocols::friends_wiiu::{ use crate::rmc::protocols::friends_wiiu::{
FriendsWiiU, RawFriendsWiiU, RawFriendsWiiUInfo, RemoteFriendsWiiU, FriendsWiiU, RawFriendsWiiU, RawFriendsWiiUInfo, RemoteFriendsWiiU,
}; };
use rnex_core::rmc::protocols::nintendo_notification::{ use crate::rmc::protocols::nintendo_notification::{
NintendoNotification, RawNintendoNotification, RawNintendoNotificationInfo, NintendoNotification, RawNintendoNotification, RawNintendoNotificationInfo,
RemoteNintendoNotification, RemoteNintendoNotification,
}; };
use rnex_core::rmc::protocols::secure::{RawSecure, RawSecureInfo, RemoteSecure, Secure}; use crate::rmc::protocols::secure::{RawSecure, RawSecureInfo, RemoteSecure, Secure};
use rnex_core::{ use crate::{
define_rmc_proto, define_rmc_proto,
kerberos::KerberosDateTime, kerberos::KerberosDateTime,
nex::common::get_station_urls, nex::common::get_station_urls,
@ -37,20 +33,25 @@ use rnex_core::{
structures::{any::Any, qresult::QResult}, structures::{any::Any, qresult::QResult},
}, },
}; };
use bytemuck::{Pod, Zeroable};
use chrono::{TimeZone, Utc};
use rnex_rmc::rmc_struct;
use sqlx::query; use sqlx::query;
use std::sync::atomic::Ordering::Relaxed; use std::sync::atomic::Ordering::Relaxed;
use tokio::spawn; use tokio::spawn;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use tokio::time::sleep; use tokio::time::sleep;
use tracing::warn;
use tracing::{error, info};
use rnex_core::rmc::protocols::friends_wiiu::{GameKey, MiiV2, PrincipalBasicInfo}; use crate::rmc::protocols::friends_wiiu::{GameKey, MiiV2, PrincipalBasicInfo};
use rnex_core::PID; use crate::PID;
use rnex_core::rmc::protocols::account_management::NintendoCreateAccountData; use crate::rmc::protocols::account_management::NintendoCreateAccountData;
use rnex_core::rmc::protocols::nintendo_notification::NintendoNotificationEvent; use crate::rmc::protocols::nintendo_notification::NintendoNotificationEvent;
use rnex_core::rmc::structures::data::Data; use crate::rmc::structures::data::Data;
use crate::executables::common::get_db; use crate::executables::common::get_db;
@ -61,9 +62,9 @@ use crate::rmc::protocols::friends_3ds::{
use crate::rmc::protocols::friends_wiiu::FriendRequestMessage; use crate::rmc::protocols::friends_wiiu::FriendRequestMessage;
use crate::rmc::protocols::nintendo_notification::NintendoNotificationEventGeneral; use crate::rmc::protocols::nintendo_notification::NintendoNotificationEventGeneral;
use crate::rmc::response::ErrorCode::FPD_InvalidArgument; use crate::rmc::response::ErrorCode::FPD_InvalidArgument;
use crate::rmc::structures::qbuffer::QBuffer;
use nex_account::grpc::ActCreateInfo; use nex_account::grpc::ActCreateInfo;
use nex_account::{derive_pid_hmac, grpc_client}; use nex_account::{derive_pid_hmac, grpc_client};
use rnex_core::rmc::structures::qbuffer::QBuffer;
define_rmc_proto!( define_rmc_proto!(
proto FriendsUser{ proto FriendsUser{
@ -223,7 +224,7 @@ impl Friends3DS for FriendsUser {
let dummy = FriendRelationship { let dummy = FriendRelationship {
data: Data {}, data: Data {},
pid: 69, pid: 69,
local_friend_code: 3268487429723707977, local_friend_code: 3_268_487_429_723_707_977,
relationship_type: 1, relationship_type: 1,
}; };
@ -258,7 +259,7 @@ impl Friends3DS for FriendsUser {
let dummy = FriendRelationship { let dummy = FriendRelationship {
data: Data {}, data: Data {},
pid: 69, pid: 69,
local_friend_code: 3268487429723707977, local_friend_code: 3_268_487_429_723_707_977,
relationship_type: 1, relationship_type: 1,
}; };
@ -275,12 +276,12 @@ impl Friends3DS for FriendsUser {
unk2: Vec<u32>, unk2: Vec<u32>,
unk3: Vec<u64>, unk3: Vec<u64>,
) -> Result<Vec<FriendRelationship>, ErrorCode> { ) -> Result<Vec<FriendRelationship>, ErrorCode> {
log::info!("params: {:?}, {:?}, {:?}", unk1, unk2, unk3); info!("params: {:?}, {:?}, {:?}", unk1, unk2, unk3);
let dummy = FriendRelationship { let dummy = FriendRelationship {
data: Data {}, data: Data {},
pid: 69, pid: 69,
local_friend_code: 3268487429723707977, local_friend_code: 3_268_487_429_723_707_977,
relationship_type: 1, relationship_type: 1,
}; };
@ -320,10 +321,10 @@ impl Friends3DS for FriendsUser {
pid: 69, pid: 69,
presence: NintendoPresence { presence: NintendoPresence {
data: Data {}, data: Data {},
changed_bit_flag: 0xFFFFFFFF, changed_bit_flag: 0xFFFF_FFFF,
game_key: rnex_core::rmc::protocols::friends_3ds::GameKey { game_key: rnex_core::rmc::protocols::friends_3ds::GameKey {
data: Data {}, data: Data {},
title_id: 1125899907457280, title_id: 1_125_899_907_457_280,
version: 2064, version: 2064,
}, },
game_mode_desctiption: "".to_string(), game_mode_desctiption: "".to_string(),
@ -365,7 +366,7 @@ impl Friends3DS for FriendsUser {
platform: 0, platform: 0,
game_key: rnex_core::rmc::protocols::friends_3ds::GameKey { game_key: rnex_core::rmc::protocols::friends_3ds::GameKey {
data: Data {}, data: Data {},
title_id: 1125899907457280, title_id: 1_125_899_907_457_280,
version: 2064, version: 2064,
}, },
message: "yo whats up".to_string(), message: "yo whats up".to_string(),
@ -603,8 +604,9 @@ impl FriendsWiiU for FriendsUser {
let users = self.fm.users.read().await; let users = self.fm.users.read().await;
let user = users.get(&friend.pid).cloned(); let user = users.get(&friend.pid).cloned();
drop(users); drop(users);
if let Some(user) = user { if let Some(user) = user
if let Some(user) = user.upgrade() { && let Some(user) = user.upgrade()
{
let online_presence = user.presence.read().await; let online_presence = user.presence.read().await;
if let Some(online_presence) = online_presence.as_ref() { if let Some(online_presence) = online_presence.as_ref() {
presence = online_presence.clone(); presence = online_presence.clone();
@ -621,7 +623,6 @@ impl FriendsWiiU for FriendsUser {
remo_friends.insert(friend.pid, Arc::downgrade(&user)); remo_friends.insert(friend.pid, Arc::downgrade(&user));
drop(remo_friends); drop(remo_friends);
} }
}
friends.push(FriendInfo { friends.push(FriendInfo {
data: Data {}, data: Data {},
@ -852,7 +853,7 @@ impl FriendsWiiU for FriendsUser {
.process_nintendo_notification_event_1(NintendoNotificationEvent { .process_nintendo_notification_event_1(NintendoNotificationEvent {
event_type: 30, event_type: 30,
sender: pid, sender: pid,
data: data, data,
}) })
.await; .await;
}); });
@ -865,7 +866,7 @@ impl FriendsWiiU for FriendsUser {
friend_request_id: i64::MAX, friend_request_id: i64::MAX,
is_recieved: true, is_recieved: true,
unk: 0, unk: 0,
message: message, message,
unk2: 1, unk2: 1,
unk3: "Dummy".into(), unk3: "Dummy".into(),
game_key, game_key,
@ -976,11 +977,10 @@ impl FriendsWiiU for FriendsUser {
return Err(ErrorCode::Core_Exception); return Err(ErrorCode::Core_Exception);
} }
} }
} else { }
println!("unknown db error occurred"); println!("unknown db error occurred");
return Err(ErrorCode::Core_Exception); return Err(ErrorCode::Core_Exception);
} }
}
}; };
let fr = friend_request_from_record!(query); let fr = friend_request_from_record!(query);
@ -1100,8 +1100,9 @@ impl FriendsWiiU for FriendsUser {
let users = self.fm.users.read().await; let users = self.fm.users.read().await;
let user = users.get(&sender).cloned(); let user = users.get(&sender).cloned();
drop(users); drop(users);
if let Some(user) = user { if let Some(user) = user
if let Some(user) = user.upgrade() { && let Some(user) = user.upgrade()
{
let mut friends = user.friend_pids.write().await; let mut friends = user.friend_pids.write().await;
friends.push(self.pid); friends.push(self.pid);
drop(friends); drop(friends);
@ -1138,7 +1139,7 @@ impl FriendsWiiU for FriendsUser {
.process_nintendo_notification_event_1(NintendoNotificationEvent { .process_nintendo_notification_event_1(NintendoNotificationEvent {
event_type: 30, event_type: 30,
sender: self.pid, sender: self.pid,
data: data, data,
}) })
.await; .await;
let online_presence = user.presence.read().await; let online_presence = user.presence.read().await;
@ -1151,7 +1152,6 @@ impl FriendsWiiU for FriendsUser {
} }
drop(online_presence); drop(online_presence);
} }
}
tx.commit().await.ok(); tx.commit().await.ok();
@ -1483,7 +1483,7 @@ impl FriendsWiiU for FriendsUser {
.execute(get_db()) .execute(get_db())
.await .await
{ {
println!("internal server error whilest updating mii: {}", e); println!("internal server error whilest updating mii: {e}");
return Err(ErrorCode::Core_Exception); return Err(ErrorCode::Core_Exception);
} }
@ -1546,7 +1546,7 @@ impl FriendsWiiU for FriendsUser {
let mut principal_infos = Vec::with_capacity(pids.len()); let mut principal_infos = Vec::with_capacity(pids.len());
for pid in pids { for pid in pids {
println!("looking up pid: {}", pid); println!("looking up pid: {pid}");
let Ok(user) = query!( let Ok(user) = query!(
"select * from nintendo_network_accounts where pid = $1", "select * from nintendo_network_accounts where pid = $1",
pid pid
@ -1594,8 +1594,8 @@ impl FriendsWiiU for FriendsUser {
.fetch_optional(get_db()) .fetch_optional(get_db())
.await .await
else { else {
println!("error whilest getting friend requests"); warn!("user requested request setting of invalid user");
return Err(ErrorCode::Core_SystemError); continue;
}; };
if denylist.is_some() { if denylist.is_some() {
@ -1712,8 +1712,6 @@ impl AccountManagement for FriendsGuest {
email: String, email: String,
auth_data: Any, auth_data: Any,
) -> Result<(PID, String), ErrorCode> { ) -> Result<(PID, String), ErrorCode> {
println!("{}, {}, {}, {}", principal_name, key, groups, email);
let nex_token = if let Ok(extra_info) = auth_data.try_get_as::<AccountExtraInfo>() { let nex_token = if let Ok(extra_info) = auth_data.try_get_as::<AccountExtraInfo>() {
extra_info.nex_token extra_info.nex_token
} else if let Ok(data) = auth_data.try_get_as::<NintendoCreateAccountData>() { } else if let Ok(data) = auth_data.try_get_as::<NintendoCreateAccountData>() {
@ -1722,15 +1720,15 @@ impl AccountManagement for FriendsGuest {
return Err(ErrorCode::Authentication_InvalidParam); return Err(ErrorCode::Authentication_InvalidParam);
}; };
let (pid, nex_key) = nex_account::decode_nexact_token(&nex_token).map_err(|e| { let (pid, nex_key) = nex_account::decode_nexact_token(&nex_token).map_err(|e| {
log::error!("failed to decode token: {}", e); error!("failed to decode token: {e}");
log::info!("{:?}", nex_token); info!("{nex_token:?}");
ErrorCode::Authentication_InvalidParam ErrorCode::Authentication_InvalidParam
})?; })?;
//let mac = derive_pid_hmac(data.nna_info.principal_basic_info.pid, &nexkey); //let mac = derive_pid_hmac(data.nna_info.principal_basic_info.pid, &nexkey);
let mut client = grpc_client().await.map_err(|e| { let mut client = grpc_client().await.map_err(|e| {
eprintln!("error occurred: {:?}", e); eprintln!("error occurred: {e:?}");
ErrorCode::Core_Unknown ErrorCode::Core_Unknown
})?; })?;
@ -1741,23 +1739,23 @@ impl AccountManagement for FriendsGuest {
pid, pid,
}; };
let nexkey = client let nex_key = client
.create_new_sequential_or_update_and_get_account(new_account) .create_new_sequential_or_update_and_get_account(new_account)
.await .await
.map_err(|_| ErrorCode::Core_Unknown)? .map_err(|_| ErrorCode::Core_Unknown)?
.into_inner(); .into_inner();
if nexkey.key.len() != 16 { if nex_key.key.len() != 16 {
log::error!("nex key was not 16 bytes long"); error!("nex key was not 16 bytes long");
return Err(ErrorCode::Authentication_InvalidParam); return Err(ErrorCode::Authentication_InvalidParam);
} }
let nexkeyarray: [u8; 16] = nexkey.key.try_into().expect("how...?"); let nexkeyarray: [u8; 16] = nex_key.key.try_into().expect("how...?");
let mac = derive_pid_hmac(pid, &nexkeyarray); let mac = derive_pid_hmac(pid, &nexkeyarray);
let hex_str = hex::encode(mac); let hex_str = hex::encode(mac);
return Ok((pid, hex_str)); Ok((pid, hex_str))
} }
} }

View file

@ -1,48 +0,0 @@
use std::borrow::Cow;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
pub fn rnex_release() -> String {
let edition_piece = if let Some(e) = option_env!("EDITION") {
format!("{}", e)
} else {
env!("FEATURESET").into()
};
format!(
"rnex {} v{}({})",
edition_piece,
env!("CARGO_PKG_VERSION"),
env!("GIT_HASH")
)
}
pub async fn with_setup(f: impl AsyncFnOnce()) {
println!("setting up logger and dotenv");
dotenv::dotenv().ok();
let _maybe_sentry = if let Ok(sentry_url) = std::env::var("SENTRY_URL") {
Some(sentry::init((
sentry_url,
sentry::ClientOptions {
release: Some(Cow::Owned(rnex_release())),
send_default_pii: true,
..Default::default()
},
)))
} else {
None
};
tracing_subscriber::registry()
.with(tracing_subscriber::fmt::layer())
.with(sentry::integrations::tracing::layer())
.try_init()
.expect("failed to init tracing subscriber");
f().await;
/*ctrlc::set_handler(||{
FORCE_EXIT.call_once_force(|_|{
println!("attempting exit");
});
}).unwrap();*/
}

View file

@ -1,189 +0,0 @@
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::sync::LazyLock;
use std::{env, result};
use thiserror::Error;
use tokio::task::JoinError;
use tonic::transport::Channel;
static API_KEY: Lazy<String> = Lazy::new(|| {
let key = env::var("ACCOUNT_GQL_API_KEY").expect("no graphql ip specified");
key
});
static CLIENT_URI: Lazy<String> = Lazy::new(|| {
env::var("ACCOUNT_GQL_URL")
.ok()
.and_then(|s| s.parse().ok())
.expect("no graphql ip specified")
});
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
RequestError(#[from] ureq::Error),
#[error(transparent)]
Json(#[from] json::Error),
#[error(transparent)]
Status(#[from] tonic::Status),
#[error(transparent)]
Transport(#[from] tonic::transport::Error),
#[error("invalid password size: {0}")]
PasswordConversion(#[from] TryFromSliceError),
#[error("something happened")]
SomethingHappened,
#[error("error joining blocking task: {0}")]
Join(#[from] JoinError),
}
pub type Result<T> = result::Result<T, Error>;
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> {
let client = nex_account::grpc_client().await.unwrap();
Ok(Self(client))
}
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();
log::warn!("prekey is {:?}", prekey);
let nexkey: [u8; 16] = prekey
.key
.try_into()
.map_err(|_| Error::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 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);
// };
// 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_user_data(&mut self , pid: u32) -> Result<GetUserDataResponse>{
let req = Request::new(GetUserDataRequest{
pid
});
let response = self.0.get_user_data(req).await?.into_inner();
Ok(response)
}*/
}
/*
pub struct Client(AccountClient<InterceptedService<Channel, InterceptorFunc>>);
impl Client{
pub async fn new() -> Result<Self>{
let channel = Channel::from_static(&*CLIENT_URI).connect().await?;
let func = Box::new(&|mut req: Request<()>|{
req.metadata_mut().insert("x-api-key", API_KEY.clone());
Ok(req)
}) as InterceptorFunc;
let client = AccountClient::with_interceptor(channel, func);
Ok(Self(client))
}
pub async fn get_nex_password(&mut self , pid: u32) -> Result<[u8; 16]>{
let req = Request::new(GetNexPasswordRequest{
pid
});
let response = self.0.get_nex_password(req).await?.into_inner();
Ok(response.password.as_bytes().try_into()?)
}
pub async fn get_user_data(&mut self , pid: u32) -> Result<GetUserDataResponse>{
let req = Request::new(GetUserDataRequest{
pid
});
let response = self.0.get_user_data(req).await?.into_inner();
Ok(response)
}
}
*/

View file

@ -1,8 +0,0 @@
//! 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<Request<()>, Status> + Send>;
pub mod account;

View file

@ -1,240 +0,0 @@
use bytemuck::{Pod, Zeroable, bytes_of};
use cfg_if::cfg_if;
use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc};
use hmac::Hmac;
use hmac::Mac;
use rc4::KeyInit;
use rc4::cipher::StreamCipherCoreWrapper;
use rc4::{Rc4, Rc4Core, StreamCipher};
use rnex_core::rmc::structures::RmcSerialize;
use std::fmt::Display;
use std::io::{Read, Write};
use typenum::U16;
use typenum::Unsigned;
use rnex_core::rmc::structures::Result;
use rnex_core::PID;
cfg_if! {
if #[cfg(feature = "friends")]{
pub type SessionLengthTy = U16;
} else {
use rc4::consts::U32;
pub type SessionLengthTy = U32;
}
}
pub const SESSION_KEY_LENGTH: usize = SessionLengthTy::USIZE;
type Md5Hmac = Hmac<md5::Md5>;
#[derive(Pod, Zeroable, Copy, Clone, Debug, Eq, PartialEq)]
#[repr(transparent)]
pub struct KerberosDateTime(pub u64);
impl Display for KerberosDateTime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}.{}.{} {}:{}:{}",
self.get_year(),
self.get_month(),
self.get_days(),
self.get_hours(),
self.get_minutes(),
self.get_seconds()
)
}
}
impl KerberosDateTime {
// this is the time which smm returned as the expriy date, we use it as a
// date so far into the future that it might as well just be never more generally
pub const PRACTICALLY_NEVER: Self = Self::new(0, 0, 0, 31, 12, 9999);
pub fn from_naive(dt: chrono::NaiveDateTime) -> Self {
use chrono::Datelike;
use chrono::Timelike;
Self::new(
dt.second() as u64,
dt.minute() as u64,
dt.hour() as u64,
dt.day() as u64,
dt.month() as u64,
dt.year() as u64,
)
}
pub const fn new(second: u64, minute: u64, hour: u64, day: u64, month: u64, year: u64) -> Self {
Self(second | (minute << 6) | (hour << 12) | (day << 17) | (month << 22) | (year << 26))
}
pub fn now() -> Self {
let now = chrono::Utc::now();
Self::new(
now.second() as u64,
now.minute() as u64,
now.hour() as u64,
now.day() as u64,
now.month() as u64,
now.year() as u64,
)
}
pub const fn get_seconds(&self) -> u8 {
(self.0 & 0b111111) as u8
}
pub const fn get_minutes(&self) -> u8 {
((self.0 >> 6) & 0b111111) as u8
}
pub const fn get_hours(&self) -> u8 {
((self.0 >> 12) & 0b11111) as u8
}
pub const fn get_days(&self) -> u8 {
((self.0 >> 17) & 0b111111) as u8
}
pub const fn get_month(&self) -> u8 {
((self.0 >> 22) & 0b1111) as u8
}
pub const fn get_year(&self) -> u64 {
(self.0 >> 26) & 0xFFFFFFFF
}
pub fn to_regular_time(&self) -> chrono::DateTime<Utc> {
let date = match NaiveDate::from_ymd_opt(
self.get_year() as i32,
self.get_month() as u32,
self.get_days() as u32,
) {
Some(v) => v,
None => {
println!("invalid datetime...: {}", self);
Default::default()
}
};
NaiveDateTime::new(
date,
NaiveTime::from_hms_opt(
self.get_hours() as u32,
self.get_minutes() as u32,
self.get_seconds() as u32,
)
.unwrap_or_default(),
)
.and_utc()
}
}
impl Default for KerberosDateTime {
fn default() -> Self {
KerberosDateTime(0)
}
}
impl RmcSerialize for KerberosDateTime {
fn serialize(&self, writer: &mut (impl Write + ?Sized)) -> Result<()> {
Ok(self.0.serialize(writer)?)
}
fn deserialize(reader: &mut (impl Read + ?Sized)) -> Result<Self> {
Ok(Self(u64::deserialize(reader)?))
}
}
#[derive(Pod, Zeroable, Copy, Clone)]
#[repr(C, packed)]
pub struct TicketInternalData {
pub issued_time: KerberosDateTime,
pub pid: PID,
pub session_key: [u8; SESSION_KEY_LENGTH],
}
impl TicketInternalData {
pub(crate) fn new(pid: PID) -> Self {
Self {
issued_time: KerberosDateTime::now(),
pid,
session_key: rand::random(),
}
}
pub(crate) fn encrypt(&self, key: [u8; 16]) -> Box<[u8]> {
let mut data = bytes_of(self).to_vec();
let mut rc4: StreamCipherCoreWrapper<Rc4Core<U16>> = Rc4::new_from_slice(&key).unwrap();
rc4.apply_keystream(&mut data);
let mut hmac = <Md5Hmac as KeyInit>::new_from_slice(&key).unwrap();
hmac.write_all(&data[..])
.expect("failed to write data to hmac");
let hmac_result = &hmac.finalize().into_bytes()[..];
data.write_all(&hmac_result)
.expect("failed to write data to vec");
data.into_boxed_slice()
}
}
#[derive(Pod, Zeroable, Debug, Copy, Clone)]
#[repr(C, packed)]
pub struct Ticket {
pub session_key: [u8; SESSION_KEY_LENGTH],
pub pid: PID,
}
impl Ticket {
pub fn encrypt(&self, key: [u8; 16], internal_data: &[u8]) -> Box<[u8]> {
let mut data = bytes_of(self).to_vec();
internal_data
.serialize(&mut data)
.expect("unable to write to vec");
let mut rc4: StreamCipherCoreWrapper<Rc4Core<U16>> = Rc4::new_from_slice(&key).unwrap();
rc4.apply_keystream(&mut data);
let mut hmac = <Md5Hmac as KeyInit>::new_from_slice(&key).unwrap();
hmac.write_all(&data[..])
.expect("failed to write data to hmac");
let hmac_result = &hmac.finalize().into_bytes()[..];
data.write_all(&hmac_result)
.expect("failed to write data to vec");
data.into_boxed_slice()
}
}
#[cfg(test)]
mod test {
use crate::kerberos::KerberosDateTime;
#[test]
fn kerberos_time_convert_test() {
let time = KerberosDateTime(135904948834);
println!("{}", time.to_regular_time().to_rfc2822());
let time = KerberosDateTime(0x9C3F3E0000);
println!(
"{}.{}.{} {}:{}:{}",
time.get_year(),
time.get_month(),
time.get_days(),
time.get_hours(),
time.get_minutes(),
time.get_seconds()
);
println!("{}", time.to_regular_time().to_rfc2822());
assert_eq!(KerberosDateTime::PRACTICALLY_NEVER, time);
println!("{}", KerberosDateTime(134222053376));
}
}

View file

@ -1,6 +0,0 @@
pub mod encryption;
pub mod socket_addr;
pub mod station_url;
pub mod ticket;
pub mod types_flags;
pub mod virtual_port;

View file

@ -1,60 +0,0 @@
use crate::define_rmc_proto;
use crate::rmc::structures::RmcSerialize;
use macros::{RmcSerialize, method_id, rmc_proto};
use rnex_core::rmc::response::ErrorCode;
use std::io;
use std::net::SocketAddrV4;
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
pub trait UnitPacketRead: AsyncRead + Unpin {
async fn read_buffer(&mut self) -> Result<Vec<u8>, io::Error> {
let mut len_raw: [u8; 4] = [0; 4];
self.read_exact(&mut len_raw).await?;
let len = u32::from_le_bytes(len_raw);
let mut vec = vec![0u8; len as _];
self.read_exact(&mut vec).await?;
Ok(vec)
}
}
impl<T: AsyncRead + Unpin> UnitPacketRead for T {}
pub trait UnitPacketWrite: AsyncWrite + Unpin {
async fn send_buffer(&mut self, data: &[u8]) -> Result<(), io::Error> {
let mut dest_data = Vec::new();
data.serialize(&mut dest_data)
.expect("ran out of memory or something");
self.write_all(&dest_data[..]).await?;
self.flush().await?;
Ok(())
}
}
impl<T: AsyncWrite + Unpin> UnitPacketWrite for T {}
#[rmc_proto(1)]
pub trait EdgeNodeManagement {
#[method_id(1)]
async fn get_url(&self, seed: u64) -> Result<SocketAddrV4, ErrorCode>;
}
define_rmc_proto!(
proto EdgeNodeHolder{
EdgeNodeManagement
}
);
#[derive(RmcSerialize, Debug)]
#[repr(u32)]
pub enum EdgeNodeHolderConnectOption {
DontRegister = 0,
Register(SocketAddrV4) = 1,
}

View file

@ -1,7 +0,0 @@
pub mod message;
pub mod structures;
pub mod response;
pub mod protocols;

View file

@ -1,27 +0,0 @@
use macros::{method_id, rmc_proto};
use rnex_core::prudp::station_url::StationUrl;
use rnex_core::rmc::response::ErrorCode;
use rnex_core::PID;
use crate::rmc::structures::any::Any;
use crate::rmc::structures::matchmake::Gathering;
#[rmc_proto(21)]
pub trait Matchmake {
#[method_id(2)]
async fn unregister_gathering(&self, gid: u32) -> Result<bool, ErrorCode>;
#[method_id(21)]
async fn find_by_single_id(&self, gid: u32) -> Result<(bool, Any<Gathering>), ErrorCode>;
#[method_id(41)]
async fn get_session_urls(&self, gid: u32) -> Result<Vec<StationUrl>, ErrorCode>;
#[method_id(42)]
async fn update_session_host(&self, gid: u32, change_owner: bool) -> Result<(), ErrorCode>;
#[method_id(44)]
async fn migrate_gathering_ownership(
&self,
gid: u32,
candidates: Vec<PID>,
participants_only: bool,
) -> Result<(), ErrorCode>;
}

View file

@ -1,371 +0,0 @@
#![allow(async_fn_in_trait)]
pub mod account_management;
pub mod auth;
pub mod datastore;
pub mod friends_3ds;
pub mod friends_wiiu;
pub mod matchmake;
pub mod matchmake_ext;
pub mod matchmake_extension;
pub mod message_delivery;
pub mod messaging;
pub mod nat_traversal;
pub mod nintendo_notification;
pub mod notifications;
pub mod ranking;
pub mod secure;
pub mod util;
use crate::result::ResultExtension;
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::RmcSerialize;
use crate::util::{SendingBufferConnection, SplittableBufferConnection};
use log::{error, info};
use std::collections::HashMap;
use std::future::Future;
use std::io::Cursor;
use std::ops::Deref;
use std::sync::Arc;
use std::time::Duration;
use thiserror::Error;
use tokio::sync::{Mutex, Notify};
use tokio::time::{Instant, sleep, sleep_until};
#[derive(Error, Debug)]
pub enum RemoteCallError {
#[error("Call to remote timed out whilst waiting on response.")]
Timeout,
#[error("A server side rmc error occurred: {0:?}")]
ServerError(ErrorCode),
#[error("Connection broke")]
ConnectionBroke,
#[error("Error reading response data: {0}")]
InvalidResponse(#[from] structures::Error),
}
pub struct RmcConnection(pub SendingBufferConnection, pub RmcResponseReceiver);
pub struct RmcResponseReceiver(Arc<Notify>, Arc<Mutex<HashMap<u32, RMCResponse>>>);
impl RmcConnection {
pub async fn make_raw_call<T: RmcSerialize>(
&self,
message: &RMCMessage,
) -> Result<T, RemoteCallError> {
self.make_raw_call_no_response(message).await?;
let data = self.1.get_response_data(message.call_id).await?;
let out = <T as RmcSerialize>::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(())
}
pub async fn disconnect(&self) {
self.0.disconnect().await;
}
}
impl RmcResponseReceiver {
// returns none if timed out
pub async fn get_response_data(&self, call_id: u32) -> Result<Vec<u8>, 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) {
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 Err(RemoteCallError::Timeout);
}
_ = notif_fut => {
continue;
}
}
}
}
}
pub trait HasRmcConnection {
fn get_connection(&self) -> &RmcConnection;
}
pub trait RemoteObject {
fn new(conn: RmcConnection) -> Self;
}
impl RemoteObject for () {
fn new(_: RmcConnection) -> Self {}
}
pub trait RmcCallable {
//type Remote: RemoteObject;
fn rmc_call(
&self,
responder: &SendingBufferConnection,
protocol_id: u16,
method_id: u32,
call_id: u32,
rest: Vec<u8>,
) -> impl std::future::Future<Output = ()> + Send;
}
#[macro_export]
macro_rules! define_rmc_proto {
(proto $name:ident{
$($protocol:path),*
}) => {
paste::paste!{
#[allow(unused_variables)]
pub trait [<Local $name>]: std::any::Any $( + [<Raw $protocol>] + $protocol)* {
async fn rmc_call(&self, remote_response_connection: &rnex_core::util::SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec<u8>){
match protocol_id{
$(
[<Raw $protocol Info>]::PROTOCOL_ID => <Self as [<Raw $protocol>]>::rmc_call_proto(self, remote_response_connection, method_id, call_id, rest).await,
)*
v => log::error!("invalid protocol called on rmc object {}", v)
}
}
}
pub struct [<Remote $name>](rnex_core::rmc::protocols::RmcConnection);
impl rnex_core::rmc::protocols::RmcPureRemoteObject for [<Remote $name>]{
fn new(conn: rnex_core::rmc::protocols::RmcConnection) -> Self{
Self(conn)
}
}
impl rnex_core::rmc::protocols::RemoteDisconnectable for [<Remote $name>]{
async fn disconnect(&self){
self.0.disconnect().await;
}
}
impl rnex_core::rmc::protocols::HasRmcConnection for [<Remote $name>]{
fn get_connection(&self) -> &rnex_core::rmc::protocols::RmcConnection{
&self.0
}
}
$(
impl [<Remote $protocol>] for [<Remote $name>]{}
)*
}
};
}
/// 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: &SendingBufferConnection,
_protocol_id: u16,
_method_id: u32,
_call_id: u32,
_rest: Vec<u8>,
) {
//todo: maybe reply with not implemented(?)
}
}
pub trait RmcPureRemoteObject {
fn new(conn: RmcConnection) -> Self;
}
pub trait RemoteDisconnectable {
async fn disconnect(&self);
}
pub struct OnlyRemote<T: RemoteDisconnectable>(T);
impl<T: RemoteDisconnectable + RmcPureRemoteObject> OnlyRemote<T> {
pub fn new(conn: RmcConnection) -> Self {
Self(T::new(conn))
}
}
impl<T: RemoteDisconnectable> Deref for OnlyRemote<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: RemoteDisconnectable> OnlyRemote<T> {
pub async fn disconnect(&self) {
self.0.disconnect().await;
}
}
impl<T: RemoteDisconnectable> RmcCallable for OnlyRemote<T> {
fn rmc_call(
&self,
_responder: &SendingBufferConnection,
_protocol_id: u16,
_method_id: u32,
_call_id: u32,
_rest: Vec<u8>,
) -> impl Future<Output = ()> + Send {
// maybe respond with not implemented or something
async {}
}
}
async fn handle_incoming<T: RmcCallable + Send + Sync + 'static>(
mut connection: SplittableBufferConnection,
remote: Arc<T>,
notify: Arc<Notify>,
incoming: Arc<Mutex<HashMap<u32, RMCResponse>>>,
) {
let sending_conn = connection.duplicate_sender();
while let Some(v) = connection.recv().await {
let Some(proto_id) = v.get(4) else {
error!("received too small rmc message.");
error!("ending rmc gateway.");
return;
};
// protocol 0 is hardcoded to be the no protocol protocol aka keepalive protocol
if *proto_id == 0 {
println!("got keepalive");
continue;
}
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;
};
let RMCMessage {
protocol_id,
method_id,
call_id,
rest_of_data,
} = message;
info!(
"RMC REQUEST: Proto: {}; Method: {}; cid: {}",
protocol_id, method_id, call_id
);
remote
.rmc_call(&sending_conn, protocol_id, method_id, call_id, rest_of_data)
.await;
}
}
info!("rmc disconnected")
}
pub fn new_rmc_gateway_connection<T: RmcCallable + Sync + Send + 'static, F>(
conn: SplittableBufferConnection,
create_internal: F,
) -> Arc<T>
where
F: FnOnce(RmcConnection) -> Arc<T>,
{
let notify = Arc::new(Notify::new());
let incoming: Arc<Mutex<HashMap<u32, RMCResponse>>> = 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 sending_conn = conn.duplicate_sender();
let exposed_object = (create_internal)(rmc_conn);
{
let exposed_object = exposed_object.clone();
tokio::spawn(async move {
handle_incoming(conn, exposed_object, notify, incoming).await;
});
tokio::spawn(async move {
while sending_conn.is_alive() {
sending_conn.send([0, 0, 0, 0, 0].to_vec()).await;
sleep(Duration::from_secs(10)).await;
}
});
}
exposed_object
}
impl<T: RmcCallable> RmcCallable for Arc<T> {
fn rmc_call(
&self,
responder: &SendingBufferConnection,
protocol_id: u16,
method_id: u32,
call_id: u32,
rest: Vec<u8>,
) -> impl Future<Output = ()> + Send {
self.as_ref()
.rmc_call(responder, protocol_id, method_id, call_id, rest)
}
}
define_rmc_proto! {
proto NoProto{}
}

View file

@ -1,23 +0,0 @@
use macros::{method_id, rmc_proto};
use rnex_core::rmc::response::ErrorCode;
#[rmc_proto(3)]
pub trait NatTraversal{
#[method_id(2)]
async fn request_probe_initiation(&self, station_to_probe: String) -> Result<(),ErrorCode>;
#[method_id(3)]
async fn request_probe_initialization_ext(&self, target_list: Vec<String>, station_to_probe: String) -> Result<(),ErrorCode>;
#[method_id(4)]
async fn report_nat_traversal_result(&self, cid: u32, result: bool, rtt: u32) -> Result<(),ErrorCode>;
#[method_id(5)]
async fn report_nat_properties(&self, nat_mapping: u32, nat_filtering: u32, rtt: u32) -> Result<(),ErrorCode>;
}
#[rmc_proto(3, NoReturn)]
pub trait NatTraversalConsole{
#[method_id(2)]
async fn request_probe_initiation(&self, station_to_probe: String);
}

View file

@ -1,41 +0,0 @@
use macros::{RmcSerialize, method_id, rmc_proto};
use rnex_core::PID;
pub mod notification_types {
pub const OWNERSHIP_CHANGED: u32 = 4000;
pub const HOST_CHANGED: u32 = 110000;
pub const REQUEST_JOIN_GATHERING: u32 = 101;
pub const END_GATHERING: u32 = 102;
}
cfg_if::cfg_if! {
if #[cfg(feature = "third-notif-param")]{
#[derive(RmcSerialize, Debug, Default, Clone)]
#[rmc_struct(0)]
pub struct NotificationEvent {
pub pid_source: PID,
pub notif_type: u32,
pub param_1: PID,
pub param_2: PID,
pub str_param: String,
pub param_3: PID,
}
} else {
#[derive(RmcSerialize, Debug, Default, Clone)]
#[rmc_struct(0)]
pub struct NotificationEvent {
pub pid_source: PID,
pub notif_type: u32,
pub param_1: PID,
pub param_2: PID,
pub str_param: String,
}
}
}
#[rmc_proto(14, NoReturn)]
pub trait Notification {
#[method_id(1)]
async fn process_notification_event(&self, event: NotificationEvent);
}

View file

@ -1,502 +0,0 @@
// i seriously dont know why the compiler is complaining about unused parentheses in the repr
// attributes but this gets it to not complain anymore
#![allow(unused_parens)]
use crate::rmc::response::ErrorCode::Core_Exception;
use crate::rmc::structures::qresult::ERROR_MASK;
use crate::util::SendingBufferConnection;
use bytemuck::bytes_of;
use log::{error, warn};
use std::io;
use std::io::{Read, Seek, Write};
use std::mem::transmute;
use v_byte_helpers::EnumTryInto;
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
#[derive(Debug, Clone)]
pub enum RMCResponseResult {
Success {
call_id: u32,
method_id: u32,
data: Vec<u8>,
},
Error {
error_code: ErrorCode,
call_id: u32,
},
}
#[derive(Debug, Clone)]
pub struct RMCResponse {
pub protocol_id: u8,
pub response_result: RMCResponseResult,
}
impl RMCResponse {
pub fn new(stream: &mut (impl Seek + Read)) -> io::Result<Self> {
// ignore the size for now this will only be used for checking
let size: 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<u8> = vec![0u8; (size - 2 - 4 - 4) as _];
stream.read(&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(()) => {
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<u8> {
generate_response(self.protocol_id, self.response_result)
.expect("failed to generate response")
}
}
pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Result<Vec<u8>> {
let size = 1
+ 1
+ match &response {
RMCResponseResult::Success { data, .. } => 4 + 4 + data.len(),
RMCResponseResult::Error { .. } => 4 + 4,
};
let mut data_out = Vec::with_capacity(size + 4);
let u32_size: u32 = size as _;
data_out.write_all(bytes_of(&u32_size))?;
data_out.push(protocol_id);
match response {
RMCResponseResult::Success {
call_id,
method_id,
data,
} => {
data_out.push(1);
data_out.write_all(bytes_of(&call_id))?;
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,
} => {
data_out.push(0);
let error_code_val: u32 = error_code.into();
let error_code_val = error_code_val | ERROR_MASK;
data_out.write_all(bytes_of(&error_code_val))?;
data_out.write_all(bytes_of(&call_id))?;
}
}
assert_eq!(data_out.len(), size + 4);
Ok(data_out)
}
pub async fn send_result(
connection: &SendingBufferConnection,
result: Result<Vec<u8>, ErrorCode>,
protocol_id: u8,
method_id: u32,
call_id: u32,
) {
let response_result = match result {
Ok(v) => RMCResponseResult::Success {
call_id,
method_id,
data: v,
},
Err(e) => {
warn!("error occurred during call: {:?}", 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: &SendingBufferConnection, rmcresponse: RMCResponse) {
connection.send(rmcresponse.to_data()).await;
}
//taken from kinnays error list directly
#[allow(nonstandard_style)]
#[repr(u32)]
#[derive(Debug, EnumTryInto, Clone, Copy)]
pub enum ErrorCode {
Core_Unknown = 0x00010001,
Core_NotImplemented = 0x00010002,
Core_InvalidPointer = 0x00010003,
Core_OperationAborted = 0x00010004,
Core_Exception = 0x00010005,
Core_AccessDenied = 0x00010006,
Core_InvalidHandle = 0x00010007,
Core_InvalidIndex = 0x00010008,
Core_OutOfMemory = 0x00010009,
Core_InvalidArgument = 0x0001000A,
Core_Timeout = 0x0001000B,
Core_InitializationFailure = 0x0001000C,
Core_CallInitiationFailure = 0x0001000D,
Core_RegistrationError = 0x0001000E,
Core_BufferOverflow = 0x0001000F,
Core_InvalidLockState = 0x00010010,
Core_InvalidSequence = 0x00010011,
Core_SystemError = 0x00010012,
Core_Cancelled = 0x00010013,
DDL_InvalidSignature = 0x00020001,
DDL_IncorrectVersion = 0x00020002,
RendezVous_ConnectionFailure = 0x00030001,
RendezVous_NotAuthenticated = 0x00030002,
RendezVous_InvalidUsername = 0x00030064,
RendezVous_InvalidPassword = 0x00030065,
RendezVous_UsernameAlreadyExists = 0x00030066,
RendezVous_AccountDisabled = 0x00030067,
RendezVous_AccountExpired = 0x00030068,
RendezVous_ConcurrentLoginDenied = 0x00030069,
RendezVous_EncryptionFailure = 0x0003006A,
RendezVous_InvalidPID = 0x0003006B,
RendezVous_MaxConnectionsReached = 0x0003006C,
RendezVous_InvalidGID = 0x0003006D,
RendezVous_InvalidControlScriptID = 0x0003006E,
RendezVous_InvalidOperationInLiveEnvironment = 0x0003006F,
RendezVous_DuplicateEntry = 0x00030070,
RendezVous_ControlScriptFailure = 0x00030071,
RendezVous_ClassNotFound = 0x00030072,
RendezVous_SessionVoid = 0x00030073,
RendezVous_DDLMismatch = 0x00030075,
RendezVous_InvalidConfiguration = 0x00030076,
RendezVous_SessionFull = 0x000300C8,
RendezVous_InvalidGatheringPassword = 0x000300C9,
RendezVous_WithoutParticipationPeriod = 0x000300CA,
RendezVous_PersistentGatheringCreationMax = 0x000300CB,
RendezVous_PersistentGatheringParticipationMax = 0x000300CC,
RendezVous_DeniedByParticipants = 0x000300CD,
RendezVous_ParticipantInBlackList = 0x000300CE,
RendezVous_GameServerMaintenance = 0x000300CF,
RendezVous_OperationPostpone = 0x000300D0,
RendezVous_OutOfRatingRange = 0x000300D1,
RendezVous_ConnectionDisconnected = 0x000300D2,
RendezVous_InvalidOperation = 0x000300D3,
RendezVous_NotParticipatedGathering = 0x000300D4,
RendezVous_MatchmakeSessionUserPasswordUnmatch = 0x000300D5,
RendezVous_MatchmakeSessionSystemPasswordUnmatch = 0x000300D6,
RendezVous_UserIsOffline = 0x000300D7,
RendezVous_AlreadyParticipatedGathering = 0x000300D8,
RendezVous_PermissionDenied = 0x000300D9,
RendezVous_NotFriend = 0x000300DA,
RendezVous_SessionClosed = 0x000300DB,
RendezVous_DatabaseTemporarilyUnavailable = 0x000300DC,
RendezVous_InvalidUniqueId = 0x000300DD,
RendezVous_MatchmakingWithdrawn = 0x000300DE,
RendezVous_LimitExceeded = 0x000300DF,
RendezVous_AccountTemporarilyDisabled = 0x000300E0,
RendezVous_PartiallyServiceClosed = 0x000300E1,
RendezVous_ConnectionDisconnectedForConcurrentLogin = 0x000300E2,
PythonCore_Exception = 0x00040001,
PythonCore_TypeError = 0x00040002,
PythonCore_IndexError = 0x00040003,
PythonCore_InvalidReference = 0x00040004,
PythonCore_CallFailure = 0x00040005,
PythonCore_MemoryError = 0x00040006,
PythonCore_KeyError = 0x00040007,
PythonCore_OperationError = 0x00040008,
PythonCore_ConversionError = 0x00040009,
PythonCore_ValidationError = 0x0004000A,
Transport_Unknown = 0x00050001,
Transport_ConnectionFailure = 0x00050002,
Transport_InvalidUrl = 0x00050003,
Transport_InvalidKey = 0x00050004,
Transport_InvalidURLType = 0x00050005,
Transport_DuplicateEndpoint = 0x00050006,
Transport_IOError = 0x00050007,
Transport_Timeout = 0x00050008,
Transport_ConnectionReset = 0x00050009,
Transport_IncorrectRemoteAuthentication = 0x0005000A,
Transport_ServerRequestError = 0x0005000B,
Transport_DecompressionFailure = 0x0005000C,
Transport_ReliableSendBufferFullFatal = 0x0005000D,
Transport_UPnPCannotInit = 0x0005000E,
Transport_UPnPCannotAddMapping = 0x0005000F,
Transport_NatPMPCannotInit = 0x00050010,
Transport_NatPMPCannotAddMapping = 0x00050011,
Transport_UnsupportedNAT = 0x00050013,
Transport_DnsError = 0x00050014,
Transport_ProxyError = 0x00050015,
Transport_DataRemaining = 0x00050016,
Transport_NoBuffer = 0x00050017,
Transport_NotFound = 0x00050018,
Transport_TemporaryServerError = 0x00050019,
Transport_PermanentServerError = 0x0005001A,
Transport_ServiceUnavailable = 0x0005001B,
Transport_ReliableSendBufferFull = 0x0005001C,
Transport_InvalidStation = 0x0005001D,
Transport_InvalidSubStreamID = 0x0005001E,
Transport_PacketBufferFull = 0x0005001F,
Transport_NatTraversalError = 0x00050020,
Transport_NatCheckError = 0x00050021,
DOCore_StationNotReached = 0x00060001,
DOCore_TargetStationDisconnect = 0x00060002,
DOCore_LocalStationLeaving = 0x00060003,
DOCore_ObjectNotFound = 0x00060004,
DOCore_InvalidRole = 0x00060005,
DOCore_CallTimeout = 0x00060006,
DOCore_RMCDispatchFailed = 0x00060007,
DOCore_MigrationInProgress = 0x00060008,
DOCore_NoAuthority = 0x00060009,
DOCore_NoTargetStationSpecified = 0x0006000A,
DOCore_JoinFailed = 0x0006000B,
DOCore_JoinDenied = 0x0006000C,
DOCore_ConnectivityTestFailed = 0x0006000D,
DOCore_Unknown = 0x0006000E,
DOCore_UnfreedReferences = 0x0006000F,
DOCore_JobTerminationFailed = 0x00060010,
DOCore_InvalidState = 0x00060011,
DOCore_FaultRecoveryFatal = 0x00060012,
DOCore_FaultRecoveryJobProcessFailed = 0x00060013,
DOCore_StationInconsitency = 0x00060014,
DOCore_AbnormalMasterState = 0x00060015,
DOCore_VersionMismatch = 0x00060016,
FPD_NotInitialized = 0x00650000,
FPD_AlreadyInitialized = 0x00650001,
FPD_NotConnected = 0x00650002,
FPD_Connected = 0x00650003,
FPD_InitializationFailure = 0x00650004,
FPD_OutOfMemory = 0x00650005,
FPD_RmcFailed = 0x00650006,
FPD_InvalidArgument = 0x00650007,
FPD_InvalidLocalAccountID = 0x00650008,
FPD_InvalidPrincipalID = 0x00650009,
FPD_InvalidLocalFriendCode = 0x0065000A,
FPD_LocalAccountNotExists = 0x0065000B,
FPD_LocalAccountNotLoaded = 0x0065000C,
FPD_LocalAccountAlreadyLoaded = 0x0065000D,
FPD_FriendAlreadyExists = 0x0065000E,
FPD_FriendNotExists = 0x0065000F,
FPD_FriendNumMax = 0x00650010,
FPD_NotFriend = 0x00650011,
FPD_FileIO = 0x00650012,
FPD_P2PInternetProhibited = 0x00650013,
FPD_Unknown = 0x00650014,
FPD_InvalidState = 0x00650015,
FPD_AddFriendProhibited = 0x00650017,
FPD_InvalidAccount = 0x00650019,
FPD_BlacklistedByMe = 0x0065001A,
FPD_FriendAlreadyAdded = 0x0065001C,
FPD_MyFriendListLimitExceed = 0x0065001D,
FPD_RequestLimitExceed = 0x0065001E,
FPD_InvalidMessageID = 0x0065001F,
FPD_MessageIsNotMine = 0x00650020,
FPD_MessageIsNotForMe = 0x00650021,
FPD_FriendRequestBlocked = 0x00650022,
FPD_NotInMyFriendList = 0x00650023,
FPD_FriendListedByMe = 0x00650024,
FPD_NotInMyBlacklist = 0x00650025,
FPD_IncompatibleAccount = 0x00650026,
FPD_BlockSettingChangeNotAllowed = 0x00650027,
FPD_SizeLimitExceeded = 0x00650028,
FPD_OperationNotAllowed = 0x00650029,
FPD_NotNetworkAccount = 0x0065002A,
FPD_NotificationNotFound = 0x0065002B,
FPD_PreferenceNotInitialized = 0x0065002C,
FPD_FriendRequestNotAllowed = 0x0065002D,
Ranking_NotInitialized = 0x00670001,
Ranking_InvalidArgument = 0x00670002,
Ranking_RegistrationError = 0x00670003,
Ranking_NotFound = 0x00670005,
Ranking_InvalidScore = 0x00670006,
Ranking_InvalidDataSize = 0x00670007,
Ranking_PermissionDenied = 0x00670009,
Ranking_Unknown = 0x0067000A,
Ranking_NotImplemented = 0x0067000B,
Authentication_NASAuthenticateError = 0x00680001,
Authentication_TokenParseError = 0x00680002,
Authentication_HttpConnectionError = 0x00680003,
Authentication_HttpDNSError = 0x00680004,
Authentication_HttpGetProxySetting = 0x00680005,
Authentication_TokenExpired = 0x00680006,
Authentication_ValidationFailed = 0x00680007,
Authentication_InvalidParam = 0x00680008,
Authentication_PrincipalIdUnmatched = 0x00680009,
Authentication_MoveCountUnmatch = 0x0068000A,
Authentication_UnderMaintenance = 0x0068000B,
Authentication_UnsupportedVersion = 0x0068000C,
Authentication_ServerVersionIsOld = 0x0068000D,
Authentication_Unknown = 0x0068000E,
Authentication_ClientVersionIsOld = 0x0068000F,
Authentication_AccountLibraryError = 0x00680010,
Authentication_ServiceNoLongerAvailable = 0x00680011,
Authentication_UnknownApplication = 0x00680012,
Authentication_ApplicationVersionIsOld = 0x00680013,
Authentication_OutOfService = 0x00680014,
Authentication_NetworkServiceLicenseRequired = 0x00680015,
Authentication_NetworkServiceLicenseSystemError = 0x00680016,
Authentication_NetworkServiceLicenseError3 = 0x00680017,
Authentication_NetworkServiceLicenseError4 = 0x00680018,
DataStore_Unknown = 0x00690001,
DataStore_InvalidArgument = 0x00690002,
DataStore_PermissionDenied = 0x00690003,
DataStore_NotFound = 0x00690004,
DataStore_AlreadyLocked = 0x00690005,
DataStore_UnderReviewing = 0x00690006,
DataStore_Expired = 0x00690007,
DataStore_InvalidCheckToken = 0x00690008,
DataStore_SystemFileError = 0x00690009,
DataStore_OverCapacity = 0x0069000A,
DataStore_OperationNotAllowed = 0x0069000B,
DataStore_InvalidPassword = 0x0069000C,
DataStore_ValueNotEqual = 0x0069000D,
ServiceItem_Unknown = 0x006C0001,
ServiceItem_InvalidArgument = 0x006C0002,
ServiceItem_EShopUnknownHttpError = 0x006C0003,
ServiceItem_EShopResponseParseError = 0x006C0004,
ServiceItem_NotOwned = 0x006C0005,
ServiceItem_InvalidLimitationType = 0x006C0006,
ServiceItem_ConsumptionRightShortage = 0x006C0007,
MatchmakeReferee_Unknown = 0x006F0001,
MatchmakeReferee_InvalidArgument = 0x006F0002,
MatchmakeReferee_AlreadyExists = 0x006F0003,
MatchmakeReferee_NotParticipatedGathering = 0x006F0004,
MatchmakeReferee_NotParticipatedRound = 0x006F0005,
MatchmakeReferee_StatsNotFound = 0x006F0006,
MatchmakeReferee_RoundNotFound = 0x006F0007,
MatchmakeReferee_RoundArbitrated = 0x006F0008,
MatchmakeReferee_RoundNotArbitrated = 0x006F0009,
Subscriber_Unknown = 0x00700001,
Subscriber_InvalidArgument = 0x00700002,
Subscriber_OverLimit = 0x00700003,
Subscriber_PermissionDenied = 0x00700004,
Ranking2_Unknown = 0x00710001,
Ranking2_InvalidArgument = 0x00710002,
Ranking2_InvalidScore = 0x00710003,
SmartDeviceVoiceChat_Unknown = 0x00720001,
SmartDeviceVoiceChat_InvalidArgument = 0x00720002,
SmartDeviceVoiceChat_InvalidResponse = 0x00720003,
SmartDeviceVoiceChat_InvalidAccessToken = 0x00720004,
SmartDeviceVoiceChat_Unauthorized = 0x00720005,
SmartDeviceVoiceChat_AccessError = 0x00720006,
SmartDeviceVoiceChat_UserNotFound = 0x00720007,
SmartDeviceVoiceChat_RoomNotFound = 0x00720008,
SmartDeviceVoiceChat_RoomNotActivated = 0x00720009,
SmartDeviceVoiceChat_ApplicationNotSupported = 0x0072000A,
SmartDeviceVoiceChat_InternalServerError = 0x0072000B,
SmartDeviceVoiceChat_ServiceUnavailable = 0x0072000C,
SmartDeviceVoiceChat_UnexpectedError = 0x0072000D,
SmartDeviceVoiceChat_UnderMaintenance = 0x0072000E,
SmartDeviceVoiceChat_ServiceNoLongerAvailable = 0x0072000F,
SmartDeviceVoiceChat_AccountTemporarilyDisabled = 0x00720010,
SmartDeviceVoiceChat_PermissionDenied = 0x00720011,
SmartDeviceVoiceChat_NetworkServiceLicenseRequired = 0x00720012,
SmartDeviceVoiceChat_AccountLibraryError = 0x00720013,
SmartDeviceVoiceChat_GameModeNotFound = 0x00720014,
Screening_Unknown = 0x00730001,
Screening_InvalidArgument = 0x00730002,
Screening_NotFound = 0x00730003,
Custom_Unknown = 0x00740001,
Ess_Unknown = 0x00750001,
Ess_GameSessionError = 0x00750002,
Ess_GameSessionMaintenance = 0x00750003,
}
impl From<super::structures::Error> for ErrorCode {
fn from(value: super::structures::Error) -> Self {
error!("rmc error occurred during method runtime: {}", value);
Self::Core_InvalidArgument
}
}
impl Into<u32> for ErrorCode {
fn into(self) -> u32 {
unsafe { transmute(self) }
}
}
#[cfg(test)]
mod test {
use crate::rmc::response::ErrorCode;
use hmac::digest::KeyInit;
use hmac::digest::consts::U5;
use rc4::{Rc4, StreamCipher};
#[test]
fn test() {
let data_orig = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 69, 4, 20];
let mut data = data_orig;
let mut rc4: Rc4<U5> = Rc4::new_from_slice("FUCKE".as_bytes().into()).expect("invalid key");
rc4.apply_keystream(&mut data);
assert_ne!(data_orig, data);
let mut rc4: Rc4<U5> = Rc4::new_from_slice("FUCKE".as_bytes().into()).expect("invalid key");
rc4.apply_keystream(&mut data);
assert_eq!(data_orig, data);
}
#[test]
fn test_enum_equivilance() {
let val: u32 = ErrorCode::Core_Unknown.into();
assert_eq!(val, 0x00010001)
}
}

View file

@ -1,19 +0,0 @@
use macros::RmcSerialize;
use rnex_core::kerberos::KerberosDateTime;
#[derive(Debug, RmcSerialize)]
#[rmc_struct(1)]
pub struct ConnectionData {
pub station_url: String,
pub special_protocols: Vec<u8>,
pub special_station_url: String,
pub date_time: KerberosDateTime,
}
#[derive(Debug, RmcSerialize)]
#[rmc_struct(1)]
pub struct ConnectionDataOld {
pub station_url: String,
pub special_protocols: Vec<u8>,
pub special_station_url: String,
}

View file

@ -1,5 +0,0 @@
use macros::RmcSerialize;
#[derive(RmcSerialize, Debug, Clone, Copy, Default)]
#[rmc_struct(0)]
pub struct Data {}

View file

@ -1,22 +0,0 @@
use bytemuck::{Pod, Zeroable};
use macros::RmcSerialize;
use rnex_core::rmc::structures::qbuffer::QBuffer;
#[derive(RmcSerialize, Debug)]
#[rmc_struct(0)]
pub struct UploadCompetitionData{
pub unk_1/*?*/: u32,
pub splatfest_id: u32,
pub unk_2/*?*/: u32,
pub score: u32,
pub team_id: u8,
pub team_win: u8,
pub is_first_upload: bool,
pub appdata: QBuffer,
}
#[derive(Copy, Clone, Pod, Zeroable)]
#[repr(C)]
struct UserData {
name: [u16; 0x10],
}

View file

@ -1,40 +0,0 @@
use crate::{PID, prudp::socket_addr::PRUDPSockAddr};
use macros::RmcSerialize;
#[derive(Debug, PartialEq, Eq, RmcSerialize)]
#[rmc_struct(0)]
pub struct ConnectionInitData {
pub prudpsock_addr: PRUDPSockAddr,
pub pid: PID,
}
#[cfg(test)]
mod test {
use std::{
io::Cursor,
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
};
use crate::{
prudp::{socket_addr::PRUDPSockAddr, virtual_port::VirtualPort},
rmc::structures::RmcSerialize,
rnex_proxy_common::ConnectionInitData,
};
#[test]
fn test() {
let data = ConnectionInitData {
prudpsock_addr: PRUDPSockAddr {
regular_socket_addr: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::BROADCAST, 19293)),
virtual_port: VirtualPort::new(10, 2),
},
pid: 100,
};
let ser = data.to_data().unwrap();
let de = ConnectionInitData::deserialize(&mut Cursor::new(ser)).unwrap();
assert_eq!(data, de);
}
}

View file

@ -1,88 +0,0 @@
use std::{
hash::{DefaultHasher, Hash, Hasher},
sync::Arc,
};
use rnex_server_api::gatherings::{
Gatherings, gathering_info_service_server::GatheringInfoService,
};
use tonic::{Request, Response, Status, async_trait};
use crate::{
nex::matchmake::{ExtendedMatchmakeSession, MatchmakeManager},
rmc::structures::matchmake::{self, MatchmakeSession},
server_api,
};
impl Into<rnex_server_api::gatherings::Gathering> for &ExtendedMatchmakeSession {
fn into(self) -> rnex_server_api::gatherings::Gathering {
let players = self.get_active_players().map(|p| *p.pid as u64).collect();
let ExtendedMatchmakeSession {
session:
MatchmakeSession {
gathering:
matchmake::Gathering {
state,
flags,
host_pid,
description,
maximum_participants,
minimum_participants,
owner_pid,
participant_policy,
policy_argument,
self_gid,
},
application_buffer,
attributes,
gamemode,
matchmake_system_type,
open_participation,
participation_count,
session_key,
},
connected_players,
} = self;
rnex_server_api::gatherings::Gathering {
players,
description: description.clone(),
flags: *flags,
host_pid: *host_pid as _,
maximum_participants: *maximum_participants as _,
minimum_participants: *minimum_participants as _,
owner_pid: *owner_pid as _,
participant_policy: *participant_policy,
policy_argument: *policy_argument,
self_gid: *self_gid,
state: *state,
application_buffer: application_buffer.clone(),
attributes: attributes.clone(),
..Default::default()
}
}
}
struct GatheringsApi(Arc<MatchmakeManager>);
#[async_trait]
impl GatheringInfoService for GatheringsApi {
async fn get_gatherings(
&self,
request: Request<()>,
) -> std::result::Result<Response<Gatherings>, Status> {
Ok(Response::new(Gatherings {
gatherings: self
.0
.sessions
.read()
.await
.keys()
.map(|gid| {
let mut hasher = DefaultHasher::new();
gid.hash(&mut hasher);
hasher.finish()
})
.collect(),
}))
}
}

View file

@ -1,36 +0,0 @@
use rnex_server_api::meta::{
ApiFeature, ApiFeatures, BuildInfo, server_meta_service_server::ServerMetaService,
};
use tonic::{Request, Response, Status, async_trait, transport::Server};
pub struct ServerMeta;
#[async_trait]
impl ServerMetaService for ServerMeta {
async fn get_build_info(&self, request: Request<()>) -> Result<Response<BuildInfo>, Status> {
Ok(Response::new(BuildInfo {
version: env!("CARGO_PKG_VERSION").to_owned(),
edition: env!("EDITION").to_owned(),
build_hash: env!("GIT_HASH").to_owned(),
feature_set: env!("FEATURESET").to_owned(),
}))
}
async fn get_api_features(
&self,
request: Request<()>,
) -> Result<Response<ApiFeatures>, Status> {
let mut api_features = vec![ApiFeature {
name: "meta".to_owned(),
needs_admin: false,
version: 0,
}];
#[cfg(not(feature = "friends"))]
api_features.push(ApiFeature {
name: "gatherings".to_owned(),
version: 0,
needs_admin: true,
});
Ok(Response::new(ApiFeatures { api_features }))
}
}

View file

@ -1,22 +0,0 @@
use std::{net::SocketAddr, str::FromStr};
use rnex_server_api::meta::server_meta_service_server::ServerMetaServiceServer;
use tonic::transport::Server;
#[cfg(not(feature = "friends"))]
use crate::nex::matchmake::MatchmakeManager;
use crate::server_api::meta::ServerMeta;
#[cfg(not(feature = "friends"))]
use std::sync::Arc;
#[cfg(not(feature = "friends"))]
mod gatherings;
mod meta;
pub async fn launch_server_api(#[cfg(not(feature = "friends"))] mmm: Arc<MatchmakeManager>) {
let mut server = Server::builder().add_service(ServerMetaServiceServer::new(ServerMeta));
server
.serve(SocketAddr::from_str("0.0.0.0:80").expect("unable to make sockaddr from ip"))
.await;
}

View file

@ -1,95 +0,0 @@
use std::marker::PhantomData;
use std::ops::{BitAnd, BitOr};
use typenum::{Cmp, IsEqual, IsLess, IsLessOrEqual, Unsigned};
/// 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<Major, Minor>`
struct Ver<MAJ: Unsigned, MIN: Unsigned> {
_phantom: PhantomData<(MAJ, MIN)>,
}
impl<MAJ: Unsigned, MIN: Unsigned> Version for Ver<MAJ, MIN> {
type Major = MAJ;
type Minor = MIN;
}
/// Represents two versions which can be compared
trait ComparableVersion<T: Version>: Version {
type IsAtLeast: SameOrUnit;
}
impl<T: Version, U: Version> ComparableVersion<T> for U
where
<T as Version>::Major: Cmp<Self::Major>,
<T as Version>::Minor: IsLessOrEqual<Self::Minor>,
<T as Version>::Major:
IsEqual<Self::Major, Output: BitAnd<typenum::LeEq<T::Minor, Self::Minor>>>,
<T as Version>::Major: IsLess<
Self::Major,
Output: BitOr<
typenum::And<
typenum::Eq<T::Major, Self::Major>,
typenum::LeEq<T::Minor, Self::Minor>,
>,
Output: SameOrUnit,
>,
>,
{
type IsAtLeast = typenum::Or<
typenum::Le<T::Major, Self::Major>,
typenum::And<typenum::Eq<T::Major, Self::Major>, typenum::LeEq<T::Minor, Self::Minor>>,
>;
}
/// Simple check for testing if the `TEST` version is at least `REQ` or higher.
type VersionAbove<REQ, TEST> = <TEST as ComparableVersion<REQ>>::IsAtLeast;
trait VersionIsAtLeast<VER: Version> {}
impl<VER: Version, T: ComparableVersion<VER, IsAtLeast = typenum::True>> VersionIsAtLeast<VER>
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<T, REQUIRED: Version, VER: Version + ComparableVersion<REQUIRED>> {
_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<T>;
}
impl SameOrUnit for typenum::True {
type Output<T> = T;
}
impl SameOrUnit for typenum::False {
type Output<T> = ();
}
impl<T, REQUIRED: Version, VER: Version + ComparableVersion<REQUIRED>> CondElemResult
for MinVersionElementHelper<T, REQUIRED, VER>
{
type Output = <<VER as ComparableVersion<REQUIRED>>::IsAtLeast as SameOrUnit>::Output<T>;
}
/// 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<T, REQUIRED, VER> =
<MinVersionElementHelper<T, REQUIRED, VER> as CondElemResult>::Output;

View file

@ -0,0 +1,16 @@
[package]
name = "rnex-auth-protos"
version = "0.1.0"
edition = "2024"
[dependencies]
cfg-if = "1.0.4"
ctor = "1.0.8"
rnex-rmc = { path = "../../rnex-rmc" }
tokio = { version = "1.52.3", features = ["time", "sync"] }
[features]
nx = []
[lints]
workspace = true

View file

@ -1,11 +1,31 @@
use crate::rmc::structures::connection_data::{ConnectionData, ConnectionDataOld};
use crate::rmc::structures::data::Data;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use macros::{RmcSerialize, method_id, rmc_proto}; use rnex_rmc::{
use rnex_core::PID; RmcSerialize,
use rnex_core::rmc::response::ErrorCode; any::Any,
use rnex_core::rmc::structures::any::Any; data::Data,
use rnex_core::rmc::structures::qresult::QResult; method_id,
qresult::QResult,
response::ErrorCode,
rmc_proto,
util::{PID, date_time::DateTime},
};
#[derive(Debug, RmcSerialize)]
#[rmc_struct(1)]
pub struct ConnectionData {
pub station_url: String,
pub special_protocols: Vec<u8>,
pub special_station_url: String,
pub date_time: DateTime,
}
#[derive(Debug, RmcSerialize)]
#[rmc_struct(1)]
pub struct ConnectionDataOld {
pub station_url: String,
pub special_protocols: Vec<u8>,
pub special_station_url: String,
}
cfg_if! { cfg_if! {
if #[cfg(feature = "nx")]{ if #[cfg(feature = "nx")]{
@ -34,6 +54,8 @@ pub trait Auth {
#[method_id(2)] #[method_id(2)]
async fn login_ex(&self, name: String, extra_data: Any) -> Result<LoginExRet, ErrorCode>; async fn login_ex(&self, name: String, extra_data: Any) -> Result<LoginExRet, ErrorCode>;
/// representation of the `RequestTicket` method(for details see the
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
#[method_id(3)] #[method_id(3)]
async fn request_ticket( async fn request_ticket(
&self, &self,
@ -41,9 +63,6 @@ pub trait Auth {
destination_pid: PID, destination_pid: PID,
) -> Result<RequestTicketRet, ErrorCode>; ) -> Result<RequestTicketRet, ErrorCode>;
/// representation of the `RequestTicket` method(for details see the
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
/// representation of the `GetPID` method(for details see the /// representation of the `GetPID` method(for details see the
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
#[method_id(4)] #[method_id(4)]

View file

@ -0,0 +1,11 @@
#![allow(async_fn_in_trait)]
use rnex_rmc::define_rmc_proto;
pub mod auth;
use auth::{Auth, RawAuth, RawAuthInfo, RemoteAuth};
define_rmc_proto!(
proto AuthProtocol{
Auth
}
);

View file

@ -0,0 +1,11 @@
[package]
name = "rnex-base-protos"
version = "0.1.0"
edition = "2024"
[dependencies]
ctor = "1.0.8"
rnex-rmc = { path = "../../rnex-rmc" }
[lints]
workspace = true

View file

@ -1,4 +1,10 @@
use macros::RmcSerialize; #![allow(async_fn_in_trait)]
use rnex_rmc::{RmcSerialize, define_rmc_proto};
pub mod secure;
pub mod util;
use secure::{RawSecure, RawSecureInfo, RemoteSecure, Secure};
use util::{RawUtility, RawUtilityInfo, RemoteUtility, Utility};
#[derive(RmcSerialize, Debug, Default, Clone)] #[derive(RmcSerialize, Debug, Default, Clone)]
#[rmc_struct(0)] #[rmc_struct(0)]
@ -16,3 +22,10 @@ impl ResultsRange {
.into() .into()
} }
} }
define_rmc_proto!(
proto BaseProtocol{
Secure,
Utility,
}
);

View file

@ -1,10 +1,7 @@
use macros::{RmcSerialize, method_id, rmc_proto}; use rnex_rmc::{
use rnex_core::prudp::station_url::StationUrl; RmcSerialize, any::Any, data::Data, method_id, qresult::QResult, response::ErrorCode,
use rnex_core::rmc::response::ErrorCode; rmc_proto, util::station_url::StationUrl,
use rnex_core::rmc::structures::qresult::QResult; };
use crate::rmc::structures::any::Any;
use crate::rmc::structures::data::Data;
#[derive(RmcSerialize)] #[derive(RmcSerialize)]
#[rmc_struct(0)] #[rmc_struct(0)]
@ -34,7 +31,7 @@ pub trait Secure {
async fn register_ex( async fn register_ex(
&self, &self,
station_urls: Vec<StationUrl>, station_urls: Vec<StationUrl>,
data: Any<Data>, data: Any,
) -> Result<(QResult, u32, StationUrl), ErrorCode>; ) -> Result<(QResult, u32, StationUrl), ErrorCode>;
#[method_id(7)] #[method_id(7)]
async fn replace_url(&self, target: StationUrl, dest: StationUrl) -> Result<(), ErrorCode>; async fn replace_url(&self, target: StationUrl, dest: StationUrl) -> Result<(), ErrorCode>;

View file

@ -1,6 +1,4 @@
use macros::{method_id, rmc_proto}; use rnex_rmc::{method_id, response::ErrorCode, rmc_proto};
use rnex_core::rmc::response::ErrorCode;
#[rmc_proto(110)] #[rmc_proto(110)]
pub trait Utility { pub trait Utility {

View file

@ -0,0 +1,12 @@
[package]
name = "rnex-ds-protos"
version = "0.1.0"
edition = "2024"
[dependencies]
ctor = "1.0.8"
rnex-rmc = { path = "../../rnex-rmc" }
rnex-base-protos = { path = "../base-protos" }
[lints]
workspace = true

View file

@ -1,10 +1,12 @@
use macros::{RmcSerialize, method_id, rmc_proto}; use rnex_base_protos::ResultsRange;
use rnex_core::PID; use rnex_rmc::{
use rnex_core::kerberos::KerberosDateTime; RmcSerialize, method_id,
use rnex_core::rmc::response::ErrorCode; qbuffer::QBuffer,
use rnex_core::rmc::structures::qbuffer::QBuffer; qresult::QResult,
use rnex_core::rmc::structures::qresult::QResult; response::ErrorCode,
use rnex_core::rmc::structures::resultsrange::ResultsRange; rmc_proto,
util::{PID, date_time::DateTime},
};
#[derive(RmcSerialize, Clone, Debug, Default)] #[derive(RmcSerialize, Clone, Debug, Default)]
#[rmc_struct(0)] #[rmc_struct(0)]
@ -55,15 +57,15 @@ pub struct GetMetaInfo {
pub meta_binary: QBuffer, pub meta_binary: QBuffer,
pub permission: Permission, pub permission: Permission,
pub del_permission: Permission, pub del_permission: Permission,
pub created_time: KerberosDateTime, pub created_time: DateTime,
pub updated_time: KerberosDateTime, pub updated_time: DateTime,
pub period: u16, pub period: u16,
pub status: u8, pub status: u8,
pub referred_count: u32, pub referred_count: u32,
pub refer_dat_id: u32, pub refer_dat_id: u32,
pub flag: u32, pub flag: u32,
pub referred_time: KerberosDateTime, pub referred_time: DateTime,
pub expire_time: KerberosDateTime, pub expire_time: DateTime,
pub tags: Vec<String>, pub tags: Vec<String>,
pub ratings: Vec<RatingInfoWithSlot>, pub ratings: Vec<RatingInfoWithSlot>,
} }
@ -199,10 +201,10 @@ pub struct DataStoreSearchParam {
pub owner_type: u8, pub owner_type: u8,
pub destination_ids: Vec<i64>, pub destination_ids: Vec<i64>,
pub data_type: u16, pub data_type: u16,
pub created_after: KerberosDateTime, pub created_after: DateTime,
pub created_before: KerberosDateTime, pub created_before: DateTime,
pub updated_after: KerberosDateTime, pub updated_after: DateTime,
pub updated_before: KerberosDateTime, pub updated_before: DateTime,
pub refer_dat_id: u32, pub refer_dat_id: u32,
pub tags: Vec<String>, pub tags: Vec<String>,
pub result_order_column: u8, pub result_order_column: u8,
@ -291,8 +293,8 @@ pub struct DataStoreGetCourseRecordResult {
pub first_pid: u32, pub first_pid: u32,
pub best_pid: u32, pub best_pid: u32,
pub best_score: i32, pub best_score: i32,
pub created_time: KerberosDateTime, pub created_time: DateTime,
pub updated_time: KerberosDateTime, pub updated_time: DateTime,
} }
#[derive(RmcSerialize, Clone)] #[derive(RmcSerialize, Clone)]

View file

@ -0,0 +1,11 @@
#![allow(async_fn_in_trait)]
pub mod datastore;
use datastore::{DataStore, RawDataStore, RawDataStoreInfo, RemoteDataStore};
use rnex_rmc::define_rmc_proto;
define_rmc_proto!(
proto DatastoreProtocol{
DataStore
}
);

View file

@ -0,0 +1,11 @@
[package]
name = "rnex-fpd-protos"
version = "0.1.0"
edition = "2024"
[dependencies]
ctor = "1.0.8"
rnex-rmc = { path = "../../rnex-rmc" }
[lints]
workspace = true

View file

@ -1,14 +1,14 @@
use macros::{RmcSerialize, method_id, rmc_proto}; use rnex_rmc::{
RmcSerialize,
use rnex_core::{ any::Any,
PID, data::Data,
rmc::{response::ErrorCode, structures::any::Any}, method_id,
response::ErrorCode,
rmc_proto,
util::{PID, date_time::DateTime},
}; };
use crate::{ use crate::friends_wiiu::NNAInfo;
kerberos::KerberosDateTime,
rmc::{protocols::friends_wiiu::NNAInfo, structures::data::Data},
};
#[derive(RmcSerialize, Debug, Clone)] #[derive(RmcSerialize, Debug, Clone)]
#[rmc_struct(0)] #[rmc_struct(0)]
@ -17,7 +17,7 @@ pub struct NintendoCreateAccountData {
pub data: Data, pub data: Data,
pub nna_info: NNAInfo, pub nna_info: NNAInfo,
pub nex_token: String, pub nex_token: String,
pub birthday: KerberosDateTime, pub birthday: DateTime,
pub unk: u64, pub unk: u64,
} }

View file

@ -1,8 +1,12 @@
use macros::{RmcSerialize, method_id, rmc_proto}; use rnex_rmc::{
RmcSerialize,
use rnex_core::rmc::{response::ErrorCode, structures::data::Data}; data::Data,
list::Buffer,
use crate::{PID, kerberos::KerberosDateTime}; method_id,
response::ErrorCode,
rmc_proto,
util::{PID, date_time::DateTime},
};
#[derive(RmcSerialize)] #[derive(RmcSerialize)]
#[rmc_struct(0)] #[rmc_struct(0)]
@ -38,7 +42,7 @@ pub struct MiiList {
pub unk1: String, pub unk1: String,
pub unk2: bool, pub unk2: bool,
pub unk3: u8, pub unk3: u8,
pub mii_data: Vec<Vec<u8>>, pub mii_data: Vec<Buffer>,
} }
#[derive(RmcSerialize, Debug)] #[derive(RmcSerialize, Debug)]
@ -56,14 +60,14 @@ pub struct PlayedGame {
#[extends] #[extends]
pub data: Data, pub data: Data,
pub game_key: GameKey, pub game_key: GameKey,
pub date_time: KerberosDateTime, pub date_time: DateTime,
} }
#[derive(RmcSerialize)] #[derive(RmcSerialize)]
#[rmc_struct(0)] #[rmc_struct(0)]
pub struct FriendInfo { pub struct FriendInfo {
pub pid: u32, pub pid: u32,
pub unk2: KerberosDateTime, pub unk2: DateTime,
} }
#[derive(RmcSerialize)] #[derive(RmcSerialize)]
@ -73,7 +77,7 @@ pub struct FriendMii {
pub data: Data, pub data: Data,
pub pid: PID, pub pid: PID,
pub mii: Mii, pub mii: Mii,
pub modified_at: KerberosDateTime, pub modified_at: DateTime,
} }
#[derive(RmcSerialize)] #[derive(RmcSerialize)]
@ -83,7 +87,7 @@ pub struct FriendMiiList {
pub data: Data, pub data: Data,
pub unk1: u32, pub unk1: u32,
pub mii_list: MiiList, pub mii_list: MiiList,
pub unk2: KerberosDateTime, pub unk2: DateTime,
} }
#[derive(RmcSerialize)] #[derive(RmcSerialize)]
@ -110,7 +114,7 @@ pub struct NintendoPresence {
pub join_game_mode: u32, pub join_game_mode: u32,
pub owner_pid: PID, pub owner_pid: PID,
pub join_group_id: u32, pub join_group_id: u32,
pub application_arg: Vec<u8>, pub application_arg: Buffer,
} }
#[derive(RmcSerialize)] #[derive(RmcSerialize)]
#[rmc_struct(0)] #[rmc_struct(0)]
@ -127,7 +131,7 @@ pub struct FriendComment {
pub data: Data, pub data: Data,
pub pid: PID, pub pid: PID,
pub comment: String, pub comment: String,
pub modified_at: KerberosDateTime, pub modified_at: DateTime,
} }
#[derive(RmcSerialize)] #[derive(RmcSerialize)]
#[rmc_struct(0)] #[rmc_struct(0)]
@ -136,7 +140,7 @@ pub struct FriendPicture {
pub data: Data, pub data: Data,
pub unk1: u32, pub unk1: u32,
pub pic_data: Vec<u32>, pub pic_data: Vec<u32>,
pub date_time: KerberosDateTime, pub date_time: DateTime,
} }
#[derive(RmcSerialize)] #[derive(RmcSerialize)]
@ -152,9 +156,9 @@ pub struct FriendPersistentInfo {
pub platform: u8, pub platform: u8,
pub game_key: GameKey, pub game_key: GameKey,
pub message: String, pub message: String,
pub msg_updated_at: KerberosDateTime, pub msg_updated_at: DateTime,
pub friended_at: KerberosDateTime, pub friended_at: DateTime,
pub last_online: KerberosDateTime, pub last_online: DateTime,
} }
#[rmc_proto(101)] #[rmc_proto(101)]

View file

@ -1,12 +1,12 @@
use macros::{RmcSerialize, method_id, rmc_proto}; use rnex_rmc::{
RmcSerialize,
use rnex_core::{kerberos::KerberosDateTime, rmc::response::ErrorCode}; data::Data,
method_id,
use rnex_core::rmc::structures::data::Data; qbuffer::QBuffer,
response::ErrorCode,
use rnex_core::PID; rmc_proto,
util::{PID, date_time::DateTime},
use crate::rmc::structures::qbuffer::QBuffer; };
#[derive(RmcSerialize, Debug, Clone, Default)] #[derive(RmcSerialize, Debug, Clone, Default)]
#[rmc_struct(0)] #[rmc_struct(0)]
@ -17,7 +17,7 @@ pub struct MiiV2 {
pub unk: u8, pub unk: u8,
pub unk2: u8, pub unk2: u8,
pub mii_data: Vec<u8>, pub mii_data: Vec<u8>,
pub date_time: KerberosDateTime, pub date_time: DateTime,
} }
#[derive(RmcSerialize, Debug, Clone, Default)] #[derive(RmcSerialize, Debug, Clone, Default)]
@ -88,7 +88,7 @@ pub struct Comment {
pub data: Data, pub data: Data,
pub unk: u8, pub unk: u8,
pub message: String, pub message: String,
pub last_changed: KerberosDateTime, pub last_changed: DateTime,
} }
#[derive(RmcSerialize, Default, Debug)] #[derive(RmcSerialize, Default, Debug)]
@ -99,8 +99,8 @@ pub struct FriendInfo {
pub nna_info: NNAInfo, pub nna_info: NNAInfo,
pub presence: NintendoPresenceV2, pub presence: NintendoPresenceV2,
pub comment: Comment, pub comment: Comment,
pub became_friends: KerberosDateTime, pub became_friends: DateTime,
pub last_online: KerberosDateTime, pub last_online: DateTime,
pub unk: u64, pub unk: u64,
} }
@ -116,8 +116,8 @@ pub struct FriendRequestMessage {
pub unk2: u8, pub unk2: u8,
pub unk3: String, pub unk3: String,
pub game_key: GameKey, pub game_key: GameKey,
pub unk4: KerberosDateTime, pub unk4: DateTime,
pub expires_on: KerberosDateTime, pub expires_on: DateTime,
} }
#[derive(RmcSerialize, Debug, Clone)] #[derive(RmcSerialize, Debug, Clone)]
@ -127,7 +127,7 @@ pub struct FriendRequest {
pub data: Data, pub data: Data,
pub basic_info: PrincipalBasicInfo, pub basic_info: PrincipalBasicInfo,
pub request_message: FriendRequestMessage, pub request_message: FriendRequestMessage,
pub sent_on: KerberosDateTime, pub sent_on: DateTime,
} }
#[derive(RmcSerialize, Debug, Default)] #[derive(RmcSerialize, Debug, Default)]
@ -137,7 +137,7 @@ pub struct BlacklistedPrincipal {
pub data: Data, pub data: Data,
pub basic_info: PrincipalBasicInfo, pub basic_info: PrincipalBasicInfo,
pub game_key: GameKey, pub game_key: GameKey,
pub since: KerberosDateTime, pub since: DateTime,
} }
#[derive(RmcSerialize, Debug)] #[derive(RmcSerialize, Debug)]
#[rmc_struct(0)] #[rmc_struct(0)]
@ -167,7 +167,7 @@ pub trait FriendsWiiU {
&self, &self,
info: NNAInfo, info: NNAInfo,
presence: NintendoPresenceV2, presence: NintendoPresenceV2,
date_time: KerberosDateTime, date_time: DateTime,
) -> Result< ) -> Result<
( (
PrincipalPreference, PrincipalPreference,
@ -200,7 +200,7 @@ pub trait FriendsWiiU {
unk2: u8, unk2: u8,
unk3: String, unk3: String,
game_key: GameKey, game_key: GameKey,
unk4: KerberosDateTime, unk4: DateTime,
) -> Result<(FriendRequest, FriendInfo), ErrorCode>; ) -> Result<(FriendRequest, FriendInfo), ErrorCode>;
#[method_id(6)] #[method_id(6)]
async fn cancel_friend_request(&self, id: u64) -> Result<(), ErrorCode>; async fn cancel_friend_request(&self, id: u64) -> Result<(), ErrorCode>;
@ -222,9 +222,9 @@ pub trait FriendsWiiU {
#[method_id(13)] #[method_id(13)]
async fn update_presence(&self, presence: NintendoPresenceV2) -> Result<(), ErrorCode>; async fn update_presence(&self, presence: NintendoPresenceV2) -> Result<(), ErrorCode>;
#[method_id(14)] #[method_id(14)]
async fn update_mii(&self, presence: MiiV2) -> Result<KerberosDateTime, ErrorCode>; async fn update_mii(&self, presence: MiiV2) -> Result<DateTime, ErrorCode>;
#[method_id(15)] #[method_id(15)]
async fn update_comment(&self, presence: Comment) -> Result<KerberosDateTime, ErrorCode>; async fn update_comment(&self, presence: Comment) -> Result<DateTime, ErrorCode>;
#[method_id(16)] #[method_id(16)]
async fn update_preference(&self, preference: PrincipalPreference) -> Result<(), ErrorCode>; async fn update_preference(&self, preference: PrincipalPreference) -> Result<(), ErrorCode>;
#[method_id(17)] #[method_id(17)]

View file

@ -0,0 +1,33 @@
#![allow(async_fn_in_trait)]
use rnex_rmc::define_rmc_proto;
pub mod account_management;
pub mod friends_3ds;
pub mod friends_wiiu;
pub mod nintendo_notification;
use account_management::{
AccountManagement, RawAccountManagement, RawAccountManagementInfo, RemoteAccountManagement,
};
use friends_3ds::{Friends3DS, RawFriends3DS, RawFriends3DSInfo, RemoteFriends3DS};
use friends_wiiu::{FriendsWiiU, RawFriendsWiiU, RawFriendsWiiUInfo, RemoteFriendsWiiU};
use nintendo_notification::{
NintendoNotification, RawNintendoNotification, RawNintendoNotificationInfo,
RemoteNintendoNotification,
};
define_rmc_proto!(
proto FriendsUser{
FriendsWiiU,
Friends3DS
}
);
define_rmc_proto!(
proto FriendRemote{
NintendoNotification
}
);
define_rmc_proto!(
proto FriendsGuest{
AccountManagement
}
);

View file

@ -1,9 +1,4 @@
use macros::{RmcSerialize, method_id, rmc_proto}; use rnex_rmc::{RmcSerialize, any::Any, data::Data, method_id, rmc_proto, util::PID};
use rnex_core::PID;
use rnex_core::rmc::structures::any::Any;
use crate::rmc::structures::data::Data;
#[derive(RmcSerialize)] #[derive(RmcSerialize)]
#[rmc_struct(0)] #[rmc_struct(0)]

View file

@ -0,0 +1,22 @@
[package]
name = "rnex-mm-protos"
version = "0.1.0"
edition = "2024"
[dependencies]
ctor = "1.0.8"
rnex-rmc = { path = "../../rnex-rmc" }
cfg-if = "1.0.4"
rnex-base-protos = { path = "../base-protos" }
[features]
third-notif-param = []
v3-3-2 = []
v3-4-0 = ["v3-3-2", "third-notif-param"]
v3-5-0 = ["v3-4-0"]
v3-8-15 = ["v3-5-0"]
v3-10-22 = ["v3-8-15"]
v4-3-11 = ["v3-8-15"]
[lints]
workspace = true

Some files were not shown because too many files have changed in this diff Show more