Merge branch 'feat/gatherings' into 'main'

Core Rewrite

See merge request perditum/rnex-splatoon!10
This commit is contained in:
andrea 2025-05-07 21:15:34 +00:00
commit c9fa7f48cd
65 changed files with 4011 additions and 939 deletions

View file

@ -1,30 +0,0 @@
name: Build
on:
push:
branches:
- main
pull_request:
branches:
- main
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: self-hosted
steps:
- name: Checkout Repository And Submodules
uses: actions/checkout@v1
with:
submodules: recursive
- name: Setup Rust
run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -q -y
- name: Build
run: . "$HOME/.cargo/env" && cargo build --verbose
- name: Run tests
run: . "$HOME/.cargo/env" && cargo test --verbose

View file

@ -1,14 +0,0 @@
name: 'Close stale issues and PRs'
on:
schedule:
- cron: '30 1 * * *'
jobs:
stale:
runs-on: debian-latest
steps:
- uses: actions/stale@v9
with:
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
days-before-stale: 10
days-before-close: 5

943
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -36,6 +36,11 @@ prost = "0.13.4"
hex = "0.4.3"
macros = { path = "macros" }
rocket = { version = "0.5.1", features = ["json", "serde_json"] }
serde = { version = "1.0.217", features = ["derive"] }
async-trait = "0.1.86"
paste = "1.0.15"
typenum = "1.18.0"
[build-dependencies]
tonic-build = "0.12.3"
@ -43,4 +48,4 @@ tonic-build = "0.12.3"
[features]
default = ["secure", "auth"]
secure = []
auth = []
auth = []

4
macros/Cargo.lock generated
View file

@ -31,9 +31,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.109"
version = "2.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
dependencies = [
"proc-macro2",
"quote",

View file

@ -10,7 +10,8 @@ edition = "2018"
proc-macro = true
[dependencies]
quote = "1"
proc-macro2 = "1.0"
syn = "1.0"
quote = "1.0.38"
proc-macro2 = "1.0.93"
syn = { version = "2.0.98", features = ["full"] }
rand = "0.9.0"

View file

@ -1,22 +1,105 @@
mod protos;
extern crate proc_macro;
use proc_macro2::TokenTree;
use proc_macro2::{Ident, Literal, Span, TokenTree};
use proc_macro::TokenStream;
use std::iter::FromIterator;
use std::mem;
use syn::{parse_macro_input, DeriveInput, Data, PathSegment, TraitItem, FieldsNamed, Fields, Visibility, Type, TypePath, Path, ImplItem, ImplItemConst, Expr, ExprLit, Lit, TypeParamBound, TraitBound, TraitBoundModifier, LitInt, Token, FnArg, Receiver, PatType, Pat, TypeInfer, TypeReference, TraitItemFn, Signature, Block, Stmt, Local, LocalInit, LitStr, PathArguments, ReturnType};
use quote::{quote, ToTokens, TokenStreamExt};
use syn::buffer::TokenBuffer;
use syn::parse::{Parse, ParseBuffer, ParseStream};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::token::Comma;
use syn::Visibility::Public;
use crate::protos::{ProtoMethodData, RmcProtocolData};
use syn::{parse_macro_input, DeriveInput, Data};
use quote::{quote, TokenStreamExt};
fn self_referece_type() -> Type {
Type::Reference(
TypeReference {
and_token: Default::default(),
lifetime: None,
mutability: None,
elem: Box::new(Type::Path(
TypePath {
qself: None,
path: Path {
leading_colon: None,
segments: {
let mut punct = Punctuated::new();
punct.push_value(PathSegment{
ident: Ident::new("Self", Span::call_site()),
arguments: PathArguments::None
});
punct
}
}
}
))
}
)
}
struct ProtoInputParams{
proto_num: LitInt,
properties: Option<(Token![,], Punctuated<Ident, Token![,]>)>
}
impl Parse for ProtoInputParams{
fn parse(input: ParseStream) -> syn::Result<Self> {
let proto_num = input.parse()?;
if let Some(seperator) = input.parse()?{
let mut punctuated = Punctuated::new();
loop {
punctuated.push_value(
input.parse()?
);
if let Some(punct) = input.parse()? {
punctuated.push_punct(punct);
} else {
return Ok(
Self{
proto_num,
properties: Some((seperator, punctuated))
}
)
}
}
} else {
Ok(
Self{
proto_num,
properties: None
}
)
}
}
}
fn single_ident_path(ident: Ident) -> Path{
Path{
segments: {
let mut punc = Punctuated::new();
punc.push(PathSegment::from(ident));
punc
},
leading_colon: None,
}
}
/// Example of user-defined [derive mode macro][1]
///
/// [1]: https://doc.rust-lang.org/reference/procedural-macros.html#derive-mode-macros
#[proc_macro_derive(RmcSerialize, attributes(extends, rmc_struct))]
pub fn rmc_serialize(input: TokenStream) -> TokenStream {
let derive_input = parse_macro_input!(input as DeriveInput);
let struct_attr = derive_input.attrs.iter()
.find(|a| a.path.segments.len() == 1 &&
a.path.segments.first().is_some_and(|p| p.ident.to_string() == "rmc_struct"));
.find(|a| a.path().segments.len() == 1 &&
a.path().segments.first().is_some_and(|p| p.ident.to_string() == "rmc_struct"));
let Data::Struct(s) = derive_input.data else {
panic!("rmc struct type MUST be a struct");
@ -29,8 +112,8 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
for f in &s.fields{
if f.attrs.iter()
.any(|a| a.path.segments.len() == 1 &&
a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends")){
.any(|a| a.path().segments.len() == 1 &&
a.path().segments.first().is_some_and(|p| p.ident.to_string() == "extends")){
continue;
}
let ident = f.ident.as_ref().unwrap();
@ -67,8 +150,8 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
for f in &s.fields{
if f.attrs.iter()
.any(|a| a.path.segments.len() == 1 &&
a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends")){
.any(|a| a.path().segments.len() == 1 &&
a.path().segments.first().is_some_and(|p| p.ident.to_string() == "extends")){
continue;
}
@ -89,23 +172,12 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
// generate base with extends stuff
let serialize_base_content = if let Some(attr) = struct_attr{
let tokens = attr.tokens.clone();
let token = tokens.into_iter().next().unwrap();
let version = match token {
TokenTree::Group(g) => {
match g.stream().into_iter().next().unwrap(){
TokenTree::Literal(l) => l,
_ => panic!("expected literal")
}
},
_ => panic!("expected group")
};
let version: Literal = attr.parse_args().expect("has to be a literal");
let pre_inner = if let Some(f) = s.fields.iter().find(|f| {
f.attrs.iter()
.any(|a| a.path.segments.len() == 1 &&
a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends"))
.any(|a| a.path().segments.len() == 1 &&
a.path().segments.first().is_some_and(|p| p.ident.to_string() == "extends"))
}){
let ident= f.ident.as_ref().unwrap();
quote! {
@ -128,22 +200,12 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
};
let deserialize_base_content = if let Some(attr) = struct_attr{
let tokens = attr.tokens.clone();
let token = tokens.into_iter().next().unwrap();
let version: Literal = attr.parse_args().expect("has to be a literal");
let version = match token {
TokenTree::Group(g) => {
match g.stream().into_iter().next().unwrap(){
TokenTree::Literal(l) => l,
_ => panic!("expected literal")
}
},
_ => panic!("expected group")
};
let pre_inner = if let Some(f) = s.fields.iter().find(|f| {
f.attrs.iter()
.any(|a| a.path.segments.len() == 1 &&
a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends"))
.any(|a| a.path().segments.len() == 1 &&
a.path().segments.first().is_some_and(|p| p.ident.to_string() == "extends"))
}){
let ident= f.ident.as_ref().unwrap();
let ty= &f.ty;
@ -181,4 +243,142 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
};
tokens.into()
}
/// Macro to automatically generate code to use a specific trait as an rmc protocol for calling to
/// remote objects or accepting incoming remote requests.
/// This is needed in order to be able to use this as part of an rmc server interface.
///
/// The protocol id which is needed to be specified is specified as a parameter to this attribute.
///
/// You will also need to assign each function inside the trait a method id by using the
/// [`macro@method_id`] attribute.
///
/// You can also specify to have the protocol to be non-returning by adding a second parameter to
/// the attribute which is just `NoReturn` e.g. ` #[rmc_proto(1, NoReturn)]`
///
/// Example
/// ```
/// // this rmc protocol has protocol id 1
/// use macros::rmc_proto;
///
/// #[rmc_proto(1)]
/// trait ExampleProtocol{
/// // this defines an rmc method with id 1
/// #[rmc_method(1)]
/// async fn hello_world_method(&self, name: String) -> Result<String, ErrorCode>;
/// }
/// ```
#[proc_macro_attribute]
pub fn rmc_proto(attr: TokenStream, input: TokenStream) -> TokenStream{
let mut params = parse_macro_input!(attr as ProtoInputParams);
let ProtoInputParams{
proto_num,
properties
} = params;
let no_return_data = properties.is_some_and(|p| p.1.iter().any(|i|{
i.to_string() == "NoReturn"
}));
let mut input = parse_macro_input!(input as syn::ItemTrait);
// gigantic ass struct initializer (to summarize this gets all of the data)
let raw_data = RmcProtocolData{
has_returns: !no_return_data,
name: input.ident.clone(),
id: proto_num,
methods: input
.items
.iter()
.filter_map(|v| match v{ TraitItem::Fn(v) => Some(v), _ => None })
.map(|func|{
let Some(attr) = func.attrs.iter()
.find(|a| a.path().segments.last().is_some_and(|s| s.ident.to_string() == "method_id")) else {
panic!( "every function inside of an rmc protocol must have a method id");
};
let Ok(id): Result<LitInt, _> = attr.parse_args() else {
panic!("todo: put a propper error message here");
};
let funcs = func
.sig
.inputs
.iter()
.skip(1)
.map(|f| {
let FnArg::Typed(t) = f else {
panic!("what");
};
let Pat::Ident(i) = &*t.pat else {
panic!("unable to handle non identifier patterns as parameter bindings");
};
(i.ident.clone(), t.ty.as_ref().clone())
}).collect();
ProtoMethodData{
id,
name: func.sig.ident.clone(),
parameters: funcs,
ret_val: func.sig.output.clone()
}
}).collect()
};
quote!{
#input
#raw_data
}.into()
}
/// Used to specify the method id of methods when making rmc protocols.
/// See [`macro@rmc_proto`] for further details.
///
/// Note: This attribute doesn't do anything by itself and just returns the thing it was attached to
/// unchanged.
#[proc_macro_attribute]
pub fn method_id(_attr: TokenStream, input: TokenStream) -> TokenStream{
// this attribute doesnt do anything by itself, see `rmc_proto`
input
}
#[proc_macro_attribute]
pub fn rmc_struct(attr: TokenStream, input: TokenStream) -> TokenStream{
let mut type_data = parse_macro_input!(input as DeriveInput);
let mut ident = parse_macro_input!(attr as syn::Path);
let last_token = ident.segments.last_mut().expect("empty path?");
last_token.ident = Ident::new(&("Local".to_owned() + &last_token.ident.to_string()), last_token.span());
let struct_name = &type_data.ident;
let out = quote!{
#type_data
impl #ident for #struct_name{
}
impl crate::rmc::protocols::RmcCallable for #struct_name{
async fn rmc_call(&self, remote_response_connection: &crate::prudp::socket::SendingConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec<u8>){
<Self as #ident>::rmc_call(self, remote_response_connection, protocol_id, method_id, call_id, rest).await;
}
}
};
out.into()
}
#[proc_macro_attribute]
pub fn connection(_attr: TokenStream, input: TokenStream) -> TokenStream{
// this attribute doesnt do anything by itself, see `rmc_struct`
input
}

291
macros/src/protos.rs Normal file
View file

@ -0,0 +1,291 @@
use proc_macro2::{Ident, Span, TokenStream, TokenTree};
use quote::{quote, ToTokens};
use syn::{LitInt, ReturnType, Token, Type};
use syn::token::{Brace, Paren, Semi};
pub struct ProtoMethodData{
pub id: LitInt,
pub name: Ident,
pub parameters: Vec<(Ident, Type)>,
pub ret_val: ReturnType,
}
/// This is a representation of the code generated by `rmc_proto` it serves to split the logic of
/// acquiring data from the actual generation to tidy up the process into first getting then
/// generating.
///
/// Use the [`ToTokens`] trait to generate the actual code.
pub struct RmcProtocolData{
pub has_returns: bool,
pub id: LitInt,
pub name: Ident,
pub methods: Vec<ProtoMethodData>
}
impl RmcProtocolData{
fn generate_raw_trait(&self, tokens: &mut TokenStream){
let Self{
has_returns,
name,
id,
methods
} = self;
// this gives us the name which the identifier of the corresponding Raw trait
let raw_name = Ident::new(&format!("Raw{}", name), name.span());
// boilerplate tokens which all raw traits need
quote!{
#[doc(hidden)]
pub trait #raw_name: #name
}.to_tokens(tokens);
// generate the body of the raw protocol trait
Brace::default().surround(tokens, |tokens|{
//generate each raw method
for method in methods{
let ProtoMethodData {
name,
parameters,
..
} = method;
let raw_name = Ident::new(&format!("raw_{}", name), name.span());
quote!{
async fn #raw_name
}.to_tokens(tokens);
Paren::default().surround(tokens, |tokens|{
quote!{ &self, data: ::std::vec::Vec<u8> }.to_tokens(tokens);
});
quote!{
-> ::core::result::Result<Vec<u8>, ErrorCode>
}.to_tokens(tokens);
Brace::default().surround(tokens, |tokens|{
quote! { let mut cursor = ::std::io::Cursor::new(data); }.to_tokens(tokens);
for (param_name, param_type) in parameters{
quote!{
let Ok(#param_name) =
<#param_type as crate::rmc::structures::RmcSerialize>::deserialize(
&mut cursor
) else {
return Err(crate::rmc::response::ErrorCode::Core_InvalidArgument);
};
}.to_tokens(tokens)
}
quote!{
let retval = self.#name
}.to_tokens(tokens);
Paren::default().surround(tokens, |tokens|{
for (paren_name, _) in parameters{
quote!{#paren_name,}.to_tokens(tokens);
}
});
quote!{
.await;
}.to_tokens(tokens);
if *has_returns{
quote!{
let retval = retval?;
let mut vec = Vec::new();
crate::rmc::structures::RmcSerialize::serialize(&retval, &mut vec).ok();
Ok(vec)
}.to_tokens(tokens);
}
})
}
quote!{
async fn rmc_call_proto(
&self,
remote_response_connection: &crate::prudp::socket::SendingConnection,
method_id: u32,
call_id: u32,
data: Vec<u8>,
)
}.to_tokens(tokens);
Brace::default().surround(tokens, |tokens|{
quote! {
let ret = match method_id
}.to_tokens(tokens);
Brace::default().surround(tokens, |tokens|{
for method in methods{
let ProtoMethodData{
id,
name,
..
} = method;
let raw_name = Ident::new(&format!("raw_{}", name), name.span());
quote!{
#id => self.#raw_name(data).await,
}.to_tokens(tokens);
}
quote!{
_ => Err(crate::rmc::response::ErrorCode::Core_NotImplemented)
}.to_tokens(tokens);
});
Semi::default().to_tokens(tokens);
if *has_returns{
quote!{
crate::rmc::response::send_result(
remote_response_connection,
ret,
#id,
method_id,
call_id,
).await
}.to_tokens(tokens);
}
});
});
quote!{
impl<T: #name> RawAuth for T{}
}.to_tokens(tokens);
}
fn generate_raw_remote_trait(&self, tokens: &mut TokenStream) {
let Self {
has_returns,
name,
id: proto_id,
methods,
..
} = self;
// this gives us the name which the identifier of the corresponding Raw trait
let remote_name = Ident::new(&format!("Remote{}", name), name.span());
// boilerplate tokens which all raw traits need
quote!{
#[doc(hidden)]
pub trait #remote_name: crate::rmc::protocols::HasRmcConnection
}.to_tokens(tokens);
// generate the body of the raw protocol trait
Brace::default().surround(tokens, |tokens|{
//generate each raw method
for method in methods{
let ProtoMethodData {
name,
parameters,
ret_val,
id: method_id,
..
} = method;
quote!{
async fn #name
}.to_tokens(tokens);
Paren::default().surround(tokens, |tokens|{
quote!{ &self, }.to_tokens(tokens);
for (param_ident, param_type) in parameters{
quote!{ #param_ident: #param_type, }.to_tokens(tokens);
}
});
quote!{
#ret_val
}.to_tokens(tokens);
Brace::default().surround(tokens, |tokens|{
quote! {
let mut send_data = Vec::new();
let mut cursor = ::std::io::Cursor::new(&mut send_data);
}.to_tokens(tokens);
for (param_name, param_type) in parameters{
quote!{
crate::result::ResultExtension::display_err_or_some(
<#param_type as crate::rmc::structures::RmcSerialize>::serialize(
&#param_name,
&mut cursor
)
).ok_or(crate::rmc::response::ErrorCode::Core_InvalidArgument)?;
}.to_tokens(tokens)
}
quote!{
let call_id = rand::random();
let message = crate::rmc::message::RMCMessage{
call_id,
method_id: #method_id,
protocol_id: #proto_id,
rest_of_data: send_data
};
let rmc_conn = <Self as crate::rmc::protocols::HasRmcConnection>::get_connection(self);
}.to_tokens(tokens);
if *has_returns{
quote!{
crate::result::ResultExtension::display_err_or_some(
rmc_conn.make_raw_call(&message).await
).ok_or(crate::rmc::response::ErrorCode::Core_Exception)
}.to_tokens(tokens);
} else {
quote!{
crate::result::ResultExtension::display_err_or_some(
rmc_conn.make_raw_call_no_response(&message).await
);
}.to_tokens(tokens);
}
})
}
});
}
fn generate_raw_info(&self, tokens: &mut TokenStream){
let Self{
name,
id,
..
} = self;
let raw_info_name = Ident::new(&format!("Raw{}Info", name), Span::call_site());
quote!{
#[doc(hidden)]
pub struct #raw_info_name;
impl #raw_info_name {
pub const PROTOCOL_ID: u16 = #id;
}
}.to_tokens(tokens);
}
}
impl ToTokens for RmcProtocolData{
fn to_tokens(&self, tokens: &mut TokenStream) {
self.generate_raw_trait(tokens);
self.generate_raw_info(tokens);
self.generate_raw_remote_trait(tokens);
}
}

View file

@ -1,3 +1,7 @@
//! Legacy grpc communication server for being able to use this with pretendos infrastructure
//! before account rs is finished.
//!
//! This WILL be deprecated as soon as account rs is in a stable state.
use tonic::{Request, Status};
type InterceptorFunc = Box<(dyn Fn(Request<()>) -> Result<Request<()>, Status> + Send)>;

View file

@ -25,7 +25,7 @@ pub fn derive_key(pid: u32, password: [u8; 16]) -> [u8; 16]{
key
}
#[derive(Pod, Zeroable, Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Pod, Zeroable, Copy, Clone, Debug, Eq, PartialEq, Default)]
#[repr(transparent)]
pub struct KerberosDateTime(pub u64);
@ -66,7 +66,7 @@ impl KerberosDateTime{
#[inline]
pub fn get_month(&self) -> u8{
((self.0 >> 22) & 0b111111) as u8
((self.0 >> 22) & 0b1111) as u8
}
#[inline]
@ -154,3 +154,17 @@ impl Ticket{
data.into_boxed_slice()
}
}
#[cfg(test)]
mod test{
use chrono::{Datelike, Utc};
use crate::kerberos::KerberosDateTime;
#[test]
fn kerberos_time_convert_test(){
let time = KerberosDateTime(135904948834);
println!("{}", time.to_regular_time().to_rfc2822());
}
}

View file

@ -1,104 +1,127 @@
#![allow(dead_code)]
#![warn(missing_docs)]
//! # Splatoon RNEX server
//!
//! This server still includes the code for rnex itself as this is the first rnex server and thus
//! also the first and only current usage of rnex, expect this and rnex to be split into seperate
//! repos soon.
use std::{env, fs};
use std::collections::BTreeMap;
use std::fs::File;
use std::net::{Ipv4Addr, SocketAddrV4};
use std::sync::Arc;
use chrono::{Local, SecondsFormat};
use log::info;
use once_cell::sync::Lazy;
use rc4::{KeyInit, Rc4, StreamCipher};
use rc4::consts::U5;
use simplelog::{ColorChoice, CombinedLogger, Config, LevelFilter, TerminalMode, TermLogger, WriteLogger};
use tokio::sync::RwLock;
use tokio::task::JoinHandle;
use crate::nex::account::Account;
use crate::protocols::{auth, block_if_maintenance};
use crate::protocols::auth::AuthProtocolConfig;
use crate::protocols::matchmake_common::MatchmakeData;
use crate::protocols::server::RMCProtocolServer;
use crate::prudp::socket::{ActiveSecureConnectionData, EncryptionPair, Socket};
use crate::prudp::packet::{VirtualPort};
use crate::nex::auth_handler::{AuthHandler, RemoteAuthClientProtocol};
use crate::prudp::packet::VirtualPort;
use crate::prudp::router::Router;
use crate::prudp::secure::{generate_secure_encryption_pairs, read_secure_connection_data};
use crate::rmc::message::RMCMessage;
use crate::rmc::structures::RmcSerialize;
use crate::prudp::secure::Secure;
use crate::prudp::sockaddr::PRUDPSockAddr;
use crate::prudp::unsecure::Unsecure;
use crate::rmc::protocols::auth::Auth;
use crate::rmc::protocols::auth::RawAuth;
use crate::rmc::protocols::auth::RawAuthInfo;
use crate::rmc::protocols::auth::RemoteAuth;
use crate::rmc::protocols::{new_rmc_gateway_connection, OnlyRemote};
use crate::rmc::response::ErrorCode;
use crate::rmc::structures::any::Any;
use crate::rmc::structures::connection_data::ConnectionData;
use crate::rmc::structures::qresult::QResult;
use chrono::{Local, SecondsFormat};
use log::{error, info};
use macros::rmc_struct;
use once_cell::sync::Lazy;
use simplelog::{
ColorChoice, CombinedLogger, Config, LevelFilter, TermLogger, TerminalMode, WriteLogger,
};
use std::fs::File;
use std::marker::PhantomData;
use std::net::{Ipv4Addr, SocketAddrV4};
use std::ops::{BitAnd, BitOr};
use std::str::FromStr;
use std::time::Duration;
use std::{env, fs};
use tokio::task::JoinHandle;
use crate::nex::user::User;
mod endianness;
mod prudp;
pub mod rmc;
mod protocols;
//mod protocols;
mod nex;
mod grpc;
mod kerberos;
mod nex;
mod result;
mod versions;
mod web;
static KERBEROS_SERVER_PASSWORD: Lazy<String> = Lazy::new(||{
static KERBEROS_SERVER_PASSWORD: Lazy<String> = Lazy::new(|| {
env::var("AUTH_SERVER_PASSWORD")
.ok()
.unwrap_or("password".to_owned())
});
static AUTH_SERVER_ACCOUNT: Lazy<Account> =
Lazy::new(|| Account::new(1, "Quazal Authentication", &KERBEROS_SERVER_PASSWORD));
static SECURE_SERVER_ACCOUNT: Lazy<Account> =
Lazy::new(|| Account::new(2, "Quazal Rendez-Vous", &KERBEROS_SERVER_PASSWORD));
static AUTH_SERVER_ACCOUNT: Lazy<Account> = Lazy::new(|| Account::new(1, "Quazal Authentication", &KERBEROS_SERVER_PASSWORD));
static SECURE_SERVER_ACCOUNT: Lazy<Account> = Lazy::new(|| Account::new(2, "Quazal Rendez-Vous", &KERBEROS_SERVER_PASSWORD));
static AUTH_SERVER_PORT: Lazy<u16> = Lazy::new(||{
static AUTH_SERVER_PORT: Lazy<u16> = Lazy::new(|| {
env::var("AUTH_SERVER_PORT")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(10000)
});
static SECURE_SERVER_PORT: Lazy<u16> = Lazy::new(||{
static SECURE_SERVER_PORT: Lazy<u16> = Lazy::new(|| {
env::var("SECURE_SERVER_PORT")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(10001)
});
static OWN_IP_PRIVATE: Lazy<Ipv4Addr> = Lazy::new(||{
static OWN_IP_PRIVATE: Lazy<Ipv4Addr> = Lazy::new(|| {
env::var("SERVER_IP")
.ok()
.and_then(|s| s.parse().ok())
.expect("no public ip specified")
});
static OWN_IP_PUBLIC: Lazy<Ipv4Addr> = Lazy::new(||{
env::var("SERVER_IP_PUBLIC")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(*OWN_IP_PRIVATE)
});
static OWN_IP_PUBLIC: Lazy<String> =
Lazy::new(|| env::var("SERVER_IP_PUBLIC").unwrap_or(OWN_IP_PRIVATE.to_string()));
static SECURE_STATION_URL: Lazy<String> = Lazy::new(||
format!("prudps:/PID=2;sid=1;stream=10;type=2;address={};port={};CID=1", *OWN_IP_PUBLIC, *SECURE_SERVER_PORT)
);
static SECURE_STATION_URL: Lazy<String> = Lazy::new(|| {
format!(
"prudps:/PID=2;sid=1;stream=10;type=2;address={};port={};CID=1",
*OWN_IP_PUBLIC, *SECURE_SERVER_PORT
)
});
#[tokio::main]
async fn main() {
CombinedLogger::init(
vec![
TermLogger::new(LevelFilter::Info, Config::default(), TerminalMode::Mixed, ColorChoice::Auto),
WriteLogger::new(LevelFilter::max(), Config::default(), {
fs::create_dir_all("log").unwrap();
let date = Local::now().to_rfc3339_opts(SecondsFormat::Secs, false);
// this fixes windows being windows
let date = date.replace(":", "-");
let filename = format!("{}.log", date);
if cfg!(windows) {
File::create(format!("log\\{}", filename)).unwrap()
} else {
File::create(format!("log/{}", filename)).unwrap()
}
})
]
).unwrap();
CombinedLogger::init(vec![
TermLogger::new(
LevelFilter::Info,
Config::default(),
TerminalMode::Mixed,
ColorChoice::Auto,
),
WriteLogger::new(LevelFilter::max(), Config::default(), {
fs::create_dir_all("log").unwrap();
let date = Local::now().to_rfc3339_opts(SecondsFormat::Secs, false);
// this fixes windows being windows
let date = date.replace(":", "-");
let filename = format!("{}.log", date);
if cfg!(windows) {
File::create(format!("log\\{}", filename)).unwrap()
} else {
File::create(format!("log/{}", filename)).unwrap()
}
}),
])
.unwrap();
dotenv::dotenv().ok();
start_servers().await;
}
/*
struct AuthServer{
router: Arc<Router>,
@ -194,7 +217,8 @@ async fn start_secure_server() -> SecureServer{
Box::new(block_if_maintenance),
Box::new(protocols::secure::bound_protocol()),
Box::new(protocols::matchmake::bound_protocol(matchmake_data.clone())),
Box::new(protocols::matchmake_extension::bound_protocol(matchmake_data))
Box::new(protocols::matchmake_extension::bound_protocol(matchmake_data)),
Box::new(protocols::nat_traversal::bound_protocol())
]));
let socket =
@ -237,74 +261,116 @@ async fn start_secure_server() -> SecureServer{
router,
socket,
}
}*/
async fn start_auth() -> JoinHandle<()> {
tokio::spawn(async {
let (router_secure, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *AUTH_SERVER_PORT))
.await
.expect("unable to start router");
let mut socket_secure = router_secure
.add_socket(VirtualPort::new(1, 10), Unsecure("6f599f81"))
.await
.expect("unable to add socket");
// let conn = socket_secure.connect(auth_sockaddr).await.unwrap();
loop {
let Some(conn) = socket_secure.accept().await else {
error!("server crashed");
return;
};
info!("new connected user!");
let _ = new_rmc_gateway_connection(conn, |_| AuthHandler {
destination_server_acct: &SECURE_SERVER_ACCOUNT,
build_name: "branch:origin/project/wup-agmj build:3_8_15_2004_0",
station_url: &SECURE_STATION_URL,
});
}
})
}
async fn start_servers(){
async fn start_secure() -> JoinHandle<()> {
tokio::spawn(async {
let (router_secure, _) =
Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *SECURE_SERVER_PORT))
.await
.expect("unable to start router");
let mut socket_secure = router_secure
.add_socket(
VirtualPort::new(1, 10),
Secure("6f599f81", &SECURE_SERVER_ACCOUNT),
)
.await
.expect("unable to add socket");
// let conn = socket_secure.connect(auth_sockaddr).await.unwrap();
loop {
let Some(conn) = socket_secure.accept().await else {
error!("server crashed");
return;
};
info!("new connected user on secure :D!");
let ip = conn.socket_addr.regular_socket_addr;
let pid = conn.user_id;
let _ = new_rmc_gateway_connection(conn, |_| User {
ip,
pid
});
}
})
}
async fn start_test() {
let addr = SocketAddrV4::new(*OWN_IP_PRIVATE, *AUTH_SERVER_PORT);
let virt_addr = VirtualPort::new(1, 10);
let prudp_addr = PRUDPSockAddr::new(addr, virt_addr);
let (router_test, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, 26969))
.await
.expect("unable to start router");
let mut socket_secure = router_test
.add_socket(VirtualPort::new(1, 10), Unsecure("6f599f81"))
.await
.expect("unable to add socket");
let conn = socket_secure.connect(prudp_addr).await.unwrap();
let remote =
new_rmc_gateway_connection(conn, |r| OnlyRemote::<RemoteAuthClientProtocol>::new(r));
let v = remote
.login_ex("1469690705".to_string(), Any::default())
.await
.unwrap();
println!("got it");
}
async fn start_servers() {
#[cfg(feature = "auth")]
let auth_server = start_auth().await;
#[cfg(feature = "secure")]
let secure_server = start_secure().await;
//let web_server = web::start_web().await;
//tokio::time::sleep(Duration::from_secs(1)).await;
//start_test().await;
#[cfg(feature = "auth")]
let auth_server = start_auth_server().await;
auth_server.await.expect("auth server crashed");
#[cfg(feature = "secure")]
let secure_server = start_secure_server().await;
#[cfg(feature = "auth")]
auth_server.join_handle.await.expect("auth server crashed");
#[cfg(feature = "secure")]
secure_server.join_handle.await.expect("auth server crashed");
}
#[cfg(test)]
mod test{
use std::io::Cursor;
use std::num::ParseIntError;
use std::str::from_utf8;
use hmac::digest::consts::U5;
use rc4::{KeyInit, Rc4, StreamCipher};
use crate::prudp::packet::PRUDPPacket;
use crate::rmc;
fn from_hex_stream(val: &str) -> Result<Vec<u8>, ParseIntError> {
let res: Result<Vec<u8>, _> = val.as_bytes()
.chunks_exact(2)
.map(|c| from_utf8(c).expect("unable to convert back to string"))
.map(|s| u8::from_str_radix(s, 16))
.collect();
res
}
#[tokio::test]
async fn simulate_packets(){
let val = from_hex_stream("ead001037d00afa1e200a5000200d9e4a4050368c18c6de4e2fb1cc40f0c020100768744db99f92c5005a061fd2a1df280cd64d5c1a565952c6befa607cbaf34661312b16db0fa6fccfb81e28b5a3a9bed02b49152bbc99cc112b7e29b9e45ec3d4b89df0fe71390883d9a927c264d07ada0de9cd28499e3ccdf3fd079e4a9848d4d783778c42da2af06106a7326634dc5bec5c3438ef18e30109839ffcc").expect("uuuuh");
let mut packet = PRUDPPacket::new(&mut Cursor::new(&val)).expect("invalid packet");
let mut rc4: Rc4<U5> =
Rc4::new_from_slice("CD&ML".as_bytes().into()).expect("invalid key");
rc4.apply_keystream(&mut packet.payload);
println!("packet: {:?}", packet);
let rmc_packet = rmc::message::RMCMessage::new(&mut Cursor::new(&packet.payload)).expect("unable to read message");
let mut a = Cursor::new(&rmc_packet.rest_of_data);
//let pid = rmc::structures::string::read(&mut a).expect("unable to read pid");
}
#[tokio::test]
async fn simulate_packets_response(){
let val = from_hex_stream("ead001032501a1af6200a500010013ffcdbc3a2ebc44efc6e38ea32a72b40201002e8644db19fe2a5005a2637d2a16f3b1fe5633037c1ed61c5aefad8afebdf2ff8600e9350fba1298b570c70f6dd647eac2d3faf0ab74ef761e2ee43dc10e249e5f91aed6813dcc04b3c707d9442b6e353b9b0b654e98f860fe5379c41d3c2a1874b7dd37ebf499e03bd2fd3e9a9203c0959feb760c38f504dcd0c9e99b17fd410657da4efa3e01c8a68ab3042d6d489788d5580778d32249cdf1fba8bf68cf4019d116ea7c580622ea1e3635139d91b44635d5e95b6c35b33898fdc0117fa6fc7162840d07a49f1e7089aa0ea65409a8ddeb2334449ba73a0ff7de462cf4a706a696de0f0521b84ae5a3f8587f3585d202d3cc0fb0451519c1b830b5e3cdd6de52e9add7325cbbf08a7c2f8b875934942b226703a22b4bc8931932dab055049051e4144b02").expect("uuuuh");
let mut packet = PRUDPPacket::new(&mut Cursor::new(&val)).expect("invalid packet");
let mut rc4: Rc4<U5> =
Rc4::new_from_slice("CD&ML".as_bytes().into()).expect("invalid key");
rc4.apply_keystream(&mut packet.payload);
println!("packet: {:?}", packet);
}
secure_server.await.expect("auth server crashed");
//web_server.await.expect("webserver crashed");
}

View file

@ -3,7 +3,6 @@ use std::sync::Arc;
use log::error;
use tokio::sync::Mutex;
use crate::protocols::auth::AuthProtocolConfig;
use crate::prudp::socket::{ConnectionData, SocketData};
use crate::rmc::message::RMCMessage;
use crate::rmc::response::{ErrorCode, RMCResponseResult};
use crate::rmc::structures::RmcSerialize;

View file

@ -7,7 +7,6 @@ use crate::grpc::account;
use crate::kerberos::KerberosDateTime;
use crate::protocols::auth::AuthProtocolConfig;
use crate::protocols::auth::ticket_generation::generate_ticket;
use crate::prudp::socket::{ConnectionData, SocketData};
use crate::rmc;
use crate::rmc::message::RMCMessage;
use crate::rmc::response::{ErrorCode, RMCResponseResult};

View file

@ -4,7 +4,6 @@ use tokio::sync::Mutex;
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
use crate::protocols::auth::{AuthProtocolConfig, get_login_data_by_pid};
use crate::protocols::auth::ticket_generation::generate_ticket;
use crate::prudp::socket::{ConnectionData, SocketData};
use crate::rmc::message::RMCMessage;
use crate::rmc::response::{ErrorCode, RMCResponseResult};
use crate::rmc::response::ErrorCode::Core_Unknown;

View file

@ -2,7 +2,6 @@ use std::io::Cursor;
use std::sync::Arc;
use tokio::sync::{Mutex, RwLock};
use crate::protocols::matchmake_common::MatchmakeData;
use crate::prudp::socket::{ConnectionData, SocketData};
use crate::rmc::message::RMCMessage;
use crate::rmc::response::{ErrorCode, RMCResponseResult};
use crate::rmc::structures::qresult::QResult;

View file

@ -0,0 +1,136 @@
use std::collections::{BTreeMap};
use std::sync::Arc;
use log::error;
use rand::random;
use tokio::sync::{Mutex, RwLock};
use crate::kerberos::KerberosDateTime;
use crate::protocols::notification::Notification;
use crate::rmc::structures::matchmake::{Gathering, MatchmakeParam, MatchmakeSession};
use crate::rmc::structures::variant::Variant;
#[derive(Default, Debug)]
pub struct ExtendedMatchmakeSession{
pub session: MatchmakeSession,
pub connected_players: Vec<Arc<Mutex<ConnectionData>>>,
}
pub struct MatchmakeData{
pub(crate) matchmake_sessions: BTreeMap<u32, Arc<Mutex<ExtendedMatchmakeSession>>>
}
impl ExtendedMatchmakeSession{
pub async fn from_matchmake_session(session: MatchmakeSession, host: &Mutex<ConnectionData>) -> Self{
let host = host.lock().await;
let ConnectionData{
active_connection_data,
..
} = &*host;
let Some(active_connection_data) = active_connection_data else{
return Default::default();
};
let ActiveConnectionData{
active_secure_connection_data,
..
} = active_connection_data;
let Some(active_secure_connection_data) = active_secure_connection_data else{
return Default::default();
};
let mm_session = MatchmakeSession{
gathering: Gathering{
self_gid: 1,
owner_pid: active_secure_connection_data.pid,
host_pid: active_secure_connection_data.pid,
..session.gathering.clone()
},
datetime: KerberosDateTime::now(),
session_key: (0..32).map(|_| random()).collect(),
matchmake_param: MatchmakeParam{
params: vec![
("@SR".to_owned(), Variant::Bool(true)),
("@GIR".to_owned(), Variant::SInt64(3))
]
},
system_password_enabled: false,
..session
};
Self{
session: mm_session,
connected_players: Default::default()
}
}
pub async fn add_player(&mut self, socket: &SocketData, conn: Arc<Mutex<ConnectionData>>, join_msg: String) {
let locked = conn.lock().await;
let Some(joining_pid) = locked.active_connection_data.as_ref()
.map(|c|
c.active_secure_connection_data.as_ref()
.map(|c| c.pid)
).flatten() else {
error!("tried to add player without secure connection");
return
};
drop(locked);
self.connected_players.push(conn);
self.session.participation_count = self.connected_players.len() as u32;
for other_connection in &self.connected_players{
let mut conn = other_connection.lock().await;
let Some(other_pid) = conn.active_connection_data.as_ref()
.map(|c|
c.active_secure_connection_data.as_ref()
.map(|c| c.pid
)
).flatten() else {
error!("tried to send connection notification to player secure connection");
return
};
/*if other_pid == self.session.gathering.owner_pid &&
joining_pid == self.session.gathering.owner_pid{
continue;
}*/
conn.send_notification(socket, Notification{
pid_source: joining_pid,
notif_type: 3001,
param_1: self.session.gathering.self_gid,
param_2: other_pid,
str_param: join_msg.clone(),
param_3: self.session.participation_count
}).await;
}
}
}
pub async fn add_matchmake_session(mm_data: Arc<RwLock<MatchmakeData>>,session: ExtendedMatchmakeSession) -> Arc<Mutex<ExtendedMatchmakeSession>> {
let gid = session.session.gathering.self_gid;
let mut mm_data = mm_data.write().await;
let session = Arc::new(Mutex::new(session));
mm_data.matchmake_sessions.insert(gid, session.clone());
session
}
impl MatchmakeData {
pub async fn try_find_session_with_criteria(&self, ) -> Option<Arc<Mutex<ExtendedMatchmakeSession>>>{
None
}
}

View file

@ -1,12 +1,15 @@
use std::io::Cursor;
use std::sync::Arc;
use std::time::Duration;
use chrono::SecondsFormat::Millis;
use log::info;
use rand::random;
use tokio::sync::{Mutex, RwLock};
use tokio::time::sleep;
use crate::protocols::matchmake_common::{ExtendedMatchmakeSession, MatchmakeData};
use crate::prudp::socket::{ConnectionData, SocketData};
use crate::rmc::message::RMCMessage;
use crate::rmc::response::{ErrorCode, RMCResponseResult};
use crate::rmc::structures::matchmake::{AutoMatchmakeParam};
use crate::rmc::structures::matchmake::{AutoMatchmakeParam, MatchmakeSession};
use crate::rmc::structures::RmcSerialize;
@ -18,7 +21,7 @@ pub async fn auto_matchmake_with_param_postpone(
mm_data: Arc<RwLock<MatchmakeData>>,
auto_matchmake_param: AutoMatchmakeParam
) -> RMCResponseResult{
println!("auto_matchmake_with_param_postpone: {:?}", auto_matchmake_param);
//println!("auto_matchmake_with_param_postpone: {:?}", auto_matchmake_param);
let locked_conn = conn.lock().await;
let Some(secure_conn) =
locked_conn.active_connection_data.as_ref().map(|a| a.active_secure_connection_data.as_ref()).flatten() else {
@ -38,35 +41,41 @@ pub async fn auto_matchmake_with_param_postpone(
// up anything else unnescesarily
drop(mm_data_read);
let gid = random();
let mut matchmake_session = auto_matchmake_param.matchmake_session.clone();
matchmake_session.gathering.self_gid = gid;
matchmake_session.gathering.host_pid = pid;
matchmake_session.gathering.owner_pid = pid;
let session =
ExtendedMatchmakeSession::from_matchmake_session(auto_matchmake_param.matchmake_session, &conn).await;
let gid = session.session.gathering.self_gid;
let mut mm_data = mm_data.write().await;
let session = Arc::new(Mutex::new(ExtendedMatchmakeSession{
session: matchmake_session.clone(),
connected_players: Vec::new()
}));
let session = Arc::new(Mutex::new(session));
mm_data.matchmake_sessions.insert(gid, session.clone());
session
};
let mut session = session.lock().await;
let mut locked_session = session.lock().await;
//todo: refactor so that this works
session.add_player(socket, conn.clone(), auto_matchmake_param.join_message).await;
{
let session = session.clone();
let socket = socket.clone();
let connection = conn.clone();
let join_msg = auto_matchmake_param.join_message.clone();
tokio::spawn(async move{
sleep(Duration::from_millis(500)).await;
println!("adding player");
let mut session = session.lock().await;
session.add_player(&socket, connection, join_msg).await;
});
}
info!("new session: {:?}", locked_session);
let mut response = Vec::new();
session.session.serialize(&mut response).expect("unable to serialize matchmake session");
locked_session.session.serialize(&mut response).expect("unable to serialize matchmake session");
rmcmessage.success_with_data(response)
}

View file

@ -0,0 +1,87 @@
use std::io::Cursor;
use std::sync::Arc;
use std::time::Duration;
use log::info;
use tokio::sync::{Mutex, RwLock};
use tokio::time::sleep;
use crate::protocols::matchmake_common::{add_matchmake_session, ExtendedMatchmakeSession, MatchmakeData};
use crate::protocols::matchmake_extension::method_auto_matchmake_with_param_postpone::auto_matchmake_with_param_postpone;
use crate::rmc::message::RMCMessage;
use crate::rmc::response::{ErrorCode, RMCResponseResult};
use crate::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam};
use crate::rmc::structures::RmcSerialize;
pub async fn create_matchmake_session_with_param(
rmcmessage: &RMCMessage,
conn: &Arc<Mutex<ConnectionData>>,
socket: &Arc<SocketData>,
mm_data: Arc<RwLock<MatchmakeData>>,
create_matchmake_session: CreateMatchmakeSessionParam
) -> RMCResponseResult {
let mut session =
ExtendedMatchmakeSession::from_matchmake_session(create_matchmake_session.matchmake_session, &conn).await;
session.session.participation_count = create_matchmake_session.participation_count as u32;
let session = add_matchmake_session(mm_data, session).await;
let mut session = session.lock().await;
session.add_player(&socket, conn.clone(), create_matchmake_session.join_message).await;
let mut response = Vec::new();
session.session.serialize(&mut response).expect("unable to serialize session");
println!("{}", hex::encode(&response));
rmcmessage.success_with_data(response)
}
pub async fn create_matchmake_session_with_param_raw_params(
rmcmessage: &RMCMessage,
socket: &Arc<SocketData>,
connection_data: &Arc<Mutex<ConnectionData>>,
data: Arc<RwLock<MatchmakeData>>
) -> RMCResponseResult{
let mut reader = Cursor::new(&rmcmessage.rest_of_data);
let Ok(matchmake_param) = CreateMatchmakeSessionParam::deserialize(&mut reader) else {
return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument);
};
create_matchmake_session_with_param(rmcmessage, connection_data, socket, data, matchmake_param).await
}
#[cfg(test)]
mod test{
use std::io::Cursor;
use crate::prudp::packet::PRUDPPacket;
use crate::rmc::message::RMCMessage;
use crate::rmc::structures::matchmake::MatchmakeSession;
use crate::rmc::structures::RmcSerialize;
#[test]
fn test(){
let data = hex::decode("ead001030000a1af12001800050002010000000000000000000000000000000000").unwrap();
let packet = PRUDPPacket::new(&mut Cursor::new(data)).unwrap();
println!("{:?}", packet);
}
#[test]
fn test_2(){
let data = hex::decode("250000008e0100000001000000001700000051b39957b90b00000100000051b3995701000001000000").unwrap();
let msg = RMCMessage::new(&mut Cursor::new(data)).unwrap();
println!("{:?}", msg)
}
}

View file

@ -3,7 +3,6 @@ use std::sync::Arc;
use log::info;
use tokio::sync::{Mutex, RwLock};
use crate::protocols::matchmake_common::MatchmakeData;
use crate::prudp::socket::{ConnectionData, SocketData};
use crate::rmc::message::RMCMessage;
use crate::rmc::response::{ErrorCode, RMCResponseResult};
use crate::rmc::structures::RmcSerialize;

View file

@ -1,5 +1,6 @@
mod method_get_playing_session;
mod method_auto_matchmake_with_param_postpone;
mod method_create_matchmake_session_with_param;
use std::sync::Arc;
use tokio::sync::{RwLock};
@ -7,10 +8,12 @@ use crate::define_protocol;
use crate::protocols::matchmake_common::MatchmakeData;
use method_get_playing_session::get_playing_session_raw_params;
use method_auto_matchmake_with_param_postpone::auto_matchmake_with_param_postpone_raw_params;
use crate::protocols::matchmake_extension::method_create_matchmake_session_with_param::create_matchmake_session_with_param_raw_params;
define_protocol!{
109(matchmake_data: Arc<RwLock<MatchmakeData>>) => {
16 => get_playing_session_raw_params,
38 => create_matchmake_session_with_param_raw_params,
40 => auto_matchmake_with_param_postpone_raw_params
}
}

View file

@ -6,7 +6,6 @@ use log::warn;
use once_cell::sync::Lazy;
use tokio::sync::Mutex;
use crate::grpc;
use crate::prudp::socket::{ConnectionData, SocketData};
use crate::rmc::message::RMCMessage;
use crate::rmc::response::{ErrorCode, RMCResponse};
@ -17,7 +16,8 @@ pub mod secure;
pub mod matchmake_extension;
pub mod matchmake_common;
pub mod matchmake;
mod notification;
pub mod notification;
pub mod nat_traversal;
static IS_MAINTENANCE: Lazy<bool> = Lazy::new(|| {
env::var("IS_MAINTENANCE")

View file

@ -0,0 +1,29 @@
use std::io::Cursor;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::{Mutex, RwLock};
use tokio::time::sleep;
use crate::protocols::matchmake_common::MatchmakeData;
use crate::rmc::message::RMCMessage;
use crate::rmc::response::{ErrorCode, RMCResponseResult};
use crate::rmc::structures::matchmake::CreateMatchmakeSessionParam;
pub async fn report_nat_properties(
rmcmessage: &RMCMessage,
socket: &Arc<SocketData>,
connection_data: &Arc<Mutex<ConnectionData>>,
) -> RMCResponseResult{
sleep(Duration::from_millis(50)).await;
rmcmessage.success_with_data(Vec::new())
}
pub async fn report_nat_properties_raw_params(
rmcmessage: &RMCMessage,
socket: &Arc<SocketData>,
connection_data: &Arc<Mutex<ConnectionData>>,
_: ()
) -> RMCResponseResult{
let mut reader = Cursor::new(&rmcmessage.rest_of_data);
report_nat_properties(rmcmessage, socket, connection_data).await
}

View file

@ -0,0 +1,10 @@
mod method_report_nat_properties;
use crate::define_protocol;
use crate::protocols::nat_traversal::method_report_nat_properties::report_nat_properties_raw_params;
define_protocol!{
3() => {
5 => report_nat_properties_raw_params
}
}

View file

@ -0,0 +1,159 @@
use macros::RmcSerialize;
use rand::random;
use crate::prudp::packet::{PRUDPHeader, PRUDPPacket, PacketOption, TypesFlags};
use crate::prudp::packet::flags::{NEED_ACK, RELIABLE};
use crate::prudp::packet::types::DATA;
use crate::rmc::message::RMCMessage;
use crate::rmc::structures::RmcSerialize;
#[derive(Debug, Eq, PartialEq, RmcSerialize)]
#[rmc_struct(0)]
pub struct Notification{
pub pid_source: u32,
pub notif_type: u32,
pub param_1: u32,
pub param_2: u32,
pub str_param: String,
pub param_3: u32,
}
impl ConnectionData{
pub async fn send_notification(&mut self, socket: &SocketData, notif: Notification){
println!("sending notification");
let mut data = Vec::new();
notif.serialize(&mut data).expect("unable to write");
let message = RMCMessage{
protocol_id: 14,
method_id: 1,
call_id: 1,
rest_of_data: data
};
println!("notif: {}", hex::encode(message.to_data()));
let mut prudp_packet = PRUDPPacket{
header: PRUDPHeader{
types_and_flags: TypesFlags::default().types(DATA).flags(NEED_ACK | RELIABLE),
source_port: socket.get_virual_port(),
destination_port: self.sock_addr.virtual_port,
..Default::default()
},
options: vec![
PacketOption::FragmentId(0),
],
payload: message.to_data(),
packet_signature: [0;16]
};
self.finish_and_send_packet_to(socket, prudp_packet).await;
}
}
#[cfg(test)]
mod test{
use std::io::Cursor;
use rand::random;
use crate::protocols::notification::Notification;
use crate::prudp::packet::{PRUDPHeader, PRUDPPacket, PacketOption, TypesFlags};
use crate::prudp::packet::flags::{NEED_ACK, RELIABLE};
use crate::prudp::packet::types::DATA;
use crate::rmc::message::RMCMessage;
use crate::rmc::structures::RmcSerialize;
#[test]
fn test(){
let data = hex::decode("ead001032900a1af62000000000000000000000000000000000000000000020100250000000e57238a6601000000001700000051b39957b90b00003661636851b3995701000001000000").unwrap();
let packet = PRUDPPacket::new(&mut Cursor::new(data)).expect("invalid packet");
println!("{:?}", packet);
let rmc = RMCMessage::new(&mut Cursor::new(packet.payload)).expect("invalid rmc message");
println!("{:?}", rmc);
let notif = Notification::deserialize(&mut Cursor::new(rmc.rest_of_data)).expect("invalid notification");
println!("{:?}", notif);
}
#[test]
fn test2(){
let data = hex::decode("250000000e57b6801001000000001700000051b39957b90b0000248a5a9851b3995701000001000000").unwrap();
//let packet = PRUDPPacket::new(&mut Cursor::new(data)).expect("invalid packet");
//println!("{:?}", packet);
let rmc = RMCMessage::new(&mut Cursor::new(data)).expect("invalid rmc message");
println!("{:?}", rmc);
let notif = Notification::deserialize(&mut Cursor::new(rmc.rest_of_data)).expect("invalid notification");
println!("{:?}", notif);
}
#[test]
fn test_rmc_serialization(){
let notif = Notification{
pid_source: random(),
notif_type: random(),
param_1: random(),
param_2: random(),
str_param: "".to_string(),
param_3: random(),
};
let mut notif_data = Vec::new();
notif.serialize(&mut notif_data).unwrap();
let message = RMCMessage{
protocol_id: 14,
method_id: 1,
call_id: random(),
rest_of_data: notif_data
};
let mut prudp_packet = PRUDPPacket{
header: PRUDPHeader{
..Default::default()
},
options: vec![
PacketOption::FragmentId(0),
],
payload: message.to_data(),
packet_signature: [0;16]
};
prudp_packet.set_sizes();
let mut packet_data: Vec<u8> = Vec::new();
prudp_packet.write_to(&mut packet_data).expect("what");
let packet_deserialized = PRUDPPacket::new(&mut Cursor::new(packet_data)).unwrap();
assert_eq!(prudp_packet, packet_deserialized);
let message_deserialized = RMCMessage::new(&mut Cursor::new(packet_deserialized.payload)).unwrap();
assert_eq!(message, message_deserialized);
let notification_deserialized = Notification::deserialize(&mut Cursor::new(message_deserialized.rest_of_data)).unwrap();
assert_eq!(notification_deserialized, notif);
}
}

View file

@ -2,7 +2,6 @@ use std::io::{Cursor, Write};
use std::sync::Arc;
use bytemuck::bytes_of;
use tokio::sync::Mutex;
use crate::prudp::socket::{ConnectionData, SocketData};
use crate::prudp::station_url::{nat_types, StationUrl};
use crate::prudp::station_url::Type::PRUDPS;
use crate::prudp::station_url::UrlOptions::{Address, NatFiltering, NatMapping, NatType, Port, PrincipalID, RVConnectionID};

View file

@ -3,11 +3,11 @@ use std::sync::Arc;
use log::error;
use tokio::sync::Mutex;
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
use crate::prudp::socket::{ConnectionData, SocketData};
use crate::rmc::message::RMCMessage;
use crate::rmc::response::{RMCResponseResult};
use crate::rmc::response::ErrorCode::Core_InvalidArgument;
use crate::rmc::structures::qbuffer;
use crate::rmc::structures::{qbuffer, RmcSerialize};
use crate::rmc::structures::qbuffer::QBuffer;
pub async fn send_report(rmcmessage: &RMCMessage, report_id: u32, data: Vec<u8>) -> RMCResponseResult{
let result = tokio::fs::write(format!("./reports/{}", report_id), data).await;
@ -17,7 +17,7 @@ pub async fn send_report(rmcmessage: &RMCMessage, report_id: u32, data: Vec<u8>)
Err(e) => error!("{}", e)
}
return rmcmessage.success_with_data(Vec::new());
rmcmessage.success_with_data(Vec::new())
}
pub async fn send_report_raw_params(rmcmessage: &RMCMessage, _: &Arc<SocketData>, _conn_data: &Arc<Mutex<ConnectionData>>, _: ()) -> RMCResponseResult{
@ -27,7 +27,7 @@ pub async fn send_report_raw_params(rmcmessage: &RMCMessage, _: &Arc<SocketData>
return rmcmessage.error_result_with_code(Core_InvalidArgument);
};
let Ok(data) = qbuffer::read(&mut reader) else {
let Ok(QBuffer(data)) = QBuffer::deserialize(&mut reader) else {
return rmcmessage.error_result_with_code(Core_InvalidArgument);
};

View file

@ -5,10 +5,11 @@ use std::sync::Arc;
use log::error;
use tokio::sync::Mutex;
use crate::prudp::packet::PRUDPPacket;
use crate::prudp::socket::{ConnectionData, SocketData};
use crate::rmc::message::RMCMessage;
use crate::rmc::response::{RMCResponse, RMCResponseResult, send_response};
use crate::rmc::response::ErrorCode::Core_NotImplemented;
use crate::web::DirectionalData::Incoming;
use crate::web::WEB_DATA;
type ContainedProtocolList = Box<[Box<dyn for<'a> Fn(&'a RMCMessage, &'a Arc<SocketData>, &'a Arc<Mutex<ConnectionData>>) -> Pin<Box<dyn Future<Output = Option<RMCResponse>> + Send + 'a>> + Send + Sync>]>;
@ -20,6 +21,13 @@ impl RMCProtocolServer{
}
pub async fn process_message(&self, packet: PRUDPPacket, socket: Arc<SocketData>, connection: Arc<Mutex<ConnectionData>>){
let locked = connection.lock().await;
let addr = locked.sock_addr.regular_socket_addr;
drop(locked);
let mut web = WEB_DATA.lock().await;
web.data.push((addr, Incoming(hex::encode(&packet.payload))));
drop(web);
let Ok(rmc) = RMCMessage::new(&mut Cursor::new(&packet.payload)) else {
error!("error reading rmc message");
return;
@ -29,7 +37,9 @@ impl RMCProtocolServer{
for proto in &self.0 {
if let Some(response) = proto(&rmc, &socket, &connection).await {
if matches!(response.response_result, RMCResponseResult::Error {..}){
error!("an rmc error occurred")
}
let mut locked = connection.lock().await;
send_response(&packet, &socket, &mut locked, response).await;
drop(locked);

167
src/nex/auth_handler.rs Normal file
View file

@ -0,0 +1,167 @@
use crate::grpc::account;
use crate::kerberos::{derive_key, KerberosDateTime, Ticket};
use crate::nex::account::Account;
use crate::rmc::protocols::auth::{Auth, RawAuth, RawAuthInfo, RemoteAuth};
use crate::rmc::response::ErrorCode;
use crate::rmc::response::ErrorCode::Core_Unknown;
use crate::rmc::structures::any::Any;
use crate::rmc::structures::connection_data::ConnectionData;
use crate::rmc::structures::qresult::QResult;
use crate::rmc::structures::RmcSerialize;
use crate::{define_rmc_proto, kerberos, rmc};
use macros::rmc_struct;
define_rmc_proto!(
proto AuthClientProtocol{
Auth
}
);
#[rmc_struct(AuthClientProtocol)]
pub struct AuthHandler {
pub destination_server_acct: &'static Account,
pub build_name: &'static str,
pub station_url: &'static str,
}
pub fn generate_ticket(
source_act_login_data: (u32, [u8; 16]),
dest_act_login_data: (u32, [u8; 16]),
) -> Box<[u8]> {
let source_key = derive_key(source_act_login_data.0, source_act_login_data.1);
let dest_key = derive_key(dest_act_login_data.0, dest_act_login_data.1);
let internal_data = kerberos::TicketInternalData::new(source_act_login_data.0);
let encrypted_inner = internal_data.encrypt(dest_key);
let encrypted_session_ticket = Ticket {
pid: dest_act_login_data.0,
session_key: internal_data.session_key,
}
.encrypt(source_key, &encrypted_inner);
encrypted_session_ticket
}
async fn get_login_data_by_pid(pid: u32) -> Option<(u32, [u8; 16])> {
let Ok(mut client) = account::Client::new().await else {
return None;
};
let Ok(passwd) = client.get_nex_password(pid).await else {
return None;
};
Some((pid, passwd))
}
impl Auth for AuthHandler {
async fn login(&self, name: String) -> Result<(), ErrorCode> {
todo!()
}
async fn login_ex(
&self,
name: String,
extra_data: Any,
) -> Result<(QResult, u32, Vec<u8>, ConnectionData, String), ErrorCode> {
let Ok(pid) = name.parse() else {
return Err(ErrorCode::Core_InvalidArgument);
};
let Ok(mut client) = account::Client::new().await else {
return Err(ErrorCode::Core_Exception);
};
let Ok(passwd) = client.get_nex_password(pid).await else {
return Err(ErrorCode::Core_Exception);
};
let source_login_data = (pid, passwd);
let destination_login_data = self.destination_server_acct.get_login_data();
let ticket = generate_ticket(source_login_data, destination_login_data);
let result = QResult::success(Core_Unknown);
let connection_data = ConnectionData {
station_url: self.station_url.to_string(),
special_station_url: "".to_string(),
//date_time: KerberosDateTime::new(1,1,1,1,1,1),
date_time: KerberosDateTime::now(),
special_protocols: Vec::new(),
};
Ok((
result,
source_login_data.0,
ticket.into(),
connection_data,
self.build_name.to_owned(),
))
}
async fn request_ticket(
&self,
source_pid: u32,
destination_pid: u32,
) -> Result<(QResult, Vec<u8>), ErrorCode> {
let Some(source_login_data) = get_login_data_by_pid(source_pid).await else {
return Err(ErrorCode::Core_Exception);
};
let desgination_login_data = if destination_pid == self.destination_server_acct.pid {
self.destination_server_acct.get_login_data()
} else {
let Some(login) = get_login_data_by_pid(destination_pid).await else {
return Err(ErrorCode::Core_Exception);
};
login
};
let result = QResult::success(Core_Unknown);
let ticket = generate_ticket(source_login_data, desgination_login_data);
Ok((result, ticket.into()))
}
async fn get_pid(&self, username: String) -> Result<u32, ErrorCode> {
Err(ErrorCode::Core_Exception)
}
async fn get_name(&self, pid: u32) -> Result<String, ErrorCode> {
Err(ErrorCode::Core_Exception)
}
}
#[cfg(test)]
mod test {
use crate::rmc::structures::connection_data::ConnectionData;
use crate::rmc::structures::qresult::QResult;
use crate::rmc::structures::RmcSerialize;
use crate::rmc::response::RMCResponse;
use std::io::Cursor;
#[test]
fn test() {
let stuff = hex::decode("200100000a0106000000028000000100010051b3995774000000a6321c7f78847c1c5e9fb825eb26bd91841f1a40d92fc694159666119cb13527f1463ac48ad42a63e6613ede67041554b1770978112e6f1f3e177a2bfc75933216dbe38f70133a1eb28e2ae32a4b5c4b0c3e3efd4c02907992e259b257270b57a9dbe7792f4721b07f8fafb9e32d50f2555c616a015c0000004b007072756470733a2f5049443d323b7369643d313b73747265616d3d31303b747970653d323b616464726573733d322e3234332e39352e3131333b706f72743d31303030313b4349443d3100000000000100002c153ba51f00000033006272616e63683a6f726967696e2f70726f6a6563742f7775702d61676d6a206275696c643a335f385f31355f323030345f3000").unwrap();
let stuff = RMCResponse::new(&mut Cursor::new(stuff)).unwrap();
let crate::rmc::response::RMCResponseResult::Success { call_id, method_id, data: stuff} = stuff.response_result else {
panic!()
};
// let stuff = hex::decode("0100010051B399577400000085F1736FCFBE93660275A3FE36FED6C2EFC57222AC99A9219CF54170A415B02DF1463AC48AD42A6307813FDE67041554B177097832ED000F892D9551A09F88E9CB0388DC1BC9527CC7384556A3287B2A349ABBF7E34A5A3EC14C2287CC7F78DA616BC3B03A035347FBD2E9A505C8EF42447CD809015F0000004E007072756470733A2F73747265616D3D31303B747970653D323B616464726573733D3139322E3136382E3137382E3132303B706F72743D31303030313B4349443D313B5049443D323B7369643D310000000000010000CDF53AA51F00000033006272616E63683A6F726967696E2F70726F6A6563742F7775702D61676D6A206275696C643A335F385F31355F323030345F3000").unwrap();
// let stuff = hex::decode("0100010051b399577400000037d3d4814d2b16dd546c94a75d32637b45f856b5abe73cf26cfaa235c5f2c1cef1463ac48ad42a637d873fde67041554b177097880cfa7e10bb810eaf686bfb0a0cf3d65b1f476ebc046d0855327986f557dca14fbb8594883c186b863f2206f22baa0309dbcc81da2f883cb2cdc12628ec7fced015c0000004b007072756470733a2f5049443d323b7369643d313b73747265616d3d31303b747970653d323b616464726573733d322e3234332e39352e3131333b706f72743d31303030313b4349443d310000000000010000b7f33aa51f00000033006272616e63683a6f726967696e2f70726f6a6563742f7775702d61676d6a206275696c643a335f385f31355f323030345f3000").unwrap();
let data = <(QResult, u32, Vec<u8>, ConnectionData, String) as RmcSerialize>::deserialize(
&mut Cursor::new(stuff),
).unwrap();
println!("data: {:?}", data);
}
}

View file

@ -1 +1,3 @@
pub mod account;
pub mod account;
pub mod auth_handler;
pub mod user;

45
src/nex/user.rs Normal file
View file

@ -0,0 +1,45 @@
use std::net::{Ipv4Addr, SocketAddrV4};
use macros::rmc_struct;
use crate::define_rmc_proto;
use crate::prudp::station_url::{nat_types, StationUrl};
use crate::prudp::station_url::Type::PRUDPS;
use crate::prudp::station_url::UrlOptions::{Address, NatFiltering, NatMapping, NatType, Port, PrincipalID, RVConnectionID};
use crate::rmc::protocols::secure::{RemoteAuth, RawAuthInfo, RawAuth, Auth};
use crate::rmc::response::ErrorCode;
use crate::rmc::structures::qresult::QResult;
define_rmc_proto!(
proto UserProtocol{
Auth
}
);
#[rmc_struct(UserProtocol)]
pub struct User {
pub pid: u32,
pub ip: SocketAddrV4,
}
impl Auth for User{
async fn register(&self, station_urls: Vec<String>) -> Result<(QResult, u32, String), ErrorCode> {
let public_station = StationUrl{
url_type: PRUDPS,
options: vec![
RVConnectionID(0),
Address(*self.ip.ip()),
Port(self.ip.port()),
NatFiltering(0),
NatMapping(0),
NatType(nat_types::BEHIND_NAT),
PrincipalID(self.pid),
]
};
let result = QResult::success(ErrorCode::Core_Unknown);
Ok((result, 0, public_station.to_string()))
}
}

View file

@ -1,63 +0,0 @@
use std::collections::{BTreeMap};
use std::sync::Arc;
use log::error;
use tokio::sync::Mutex;
use crate::protocols::notification::Notification;
use crate::prudp::socket::{ConnectionData, SocketData};
use crate::rmc::structures::matchmake::MatchmakeSession;
pub struct ExtendedMatchmakeSession{
pub session: MatchmakeSession,
pub connected_players: Vec<Arc<Mutex<ConnectionData>>>,
}
pub struct MatchmakeData{
pub(crate) matchmake_sessions: BTreeMap<u32, Arc<Mutex<ExtendedMatchmakeSession>>>
}
impl ExtendedMatchmakeSession{
pub async fn add_player(&mut self, socket: &SocketData, conn: Arc<Mutex<ConnectionData>>, join_msg: String) {
let Some(pid) = conn.lock().await.active_connection_data.as_ref()
.map(|c|
c.active_secure_connection_data.as_ref()
.map(|c| c.pid
)
).flatten() else {
error!("tried to add player without secure connection");
return
};
self.connected_players.push(conn);
for conn in &self.connected_players{
let Some(other_pid) = conn.lock().await.active_connection_data.as_ref()
.map(|c|
c.active_secure_connection_data.as_ref()
.map(|c| c.pid
)
).flatten() else {
error!("tried to send connection notification to player secure connection");
return
};
let mut conn = conn.lock().await;
conn.send_notification(socket, Notification{
pid_source: pid,
notif_type: 3001,
param_1: self.session.gathering.self_gid,
param_2: other_pid,
str_param: join_msg.clone(),
}).await;
}
}
}
impl MatchmakeData {
pub async fn try_find_session_with_criteria(&self, ) -> Option<Arc<Mutex<ExtendedMatchmakeSession>>>{
None
}
}

View file

@ -1,48 +0,0 @@
use macros::RmcSerialize;
use rand::random;
use crate::prudp::packet::{PRUDPHeader, PRUDPPacket, TypesFlags};
use crate::prudp::packet::flags::{NEED_ACK, RELIABLE};
use crate::prudp::packet::types::DATA;
use crate::prudp::socket::{ConnectionData, SocketData};
use crate::rmc::message::RMCMessage;
use crate::rmc::structures::RmcSerialize;
#[derive(RmcSerialize)]
#[rmc_struct(0)]
pub struct Notification{
pub pid_source: u32,
pub notif_type: u32,
pub param_1: u32,
pub param_2: u32,
pub str_param: String,
}
impl ConnectionData{
pub async fn send_notification(&mut self, socket: &SocketData, notif: Notification){
let mut data = Vec::new();
notif.serialize(&mut data).expect("unable to write");
let message = RMCMessage{
protocol_id: 0xE,
method_id: 1,
call_id: random(),
rest_of_data: data
};
let prudp_packet = PRUDPPacket{
header: PRUDPHeader{
types_and_flags: TypesFlags::default().types(DATA).flags(NEED_ACK | RELIABLE),
source_port: socket.get_virual_port(),
destination_port: self.sock_addr.virtual_port,
..Default::default()
},
options: Vec::new(),
payload: message.to_data(),
packet_signature: [0;16]
};
self.finish_and_send_packet_to(socket, prudp_packet).await;
}
}

View file

@ -2,6 +2,7 @@ pub mod packet;
pub mod router;
pub mod socket;
mod auth_module;
mod sockaddr;
pub mod sockaddr;
pub mod station_url;
pub mod secure;
pub mod station_url;
pub mod unsecure;

View file

@ -40,7 +40,7 @@ pub enum Error {
pub type Result<T> = std::result::Result<T, Error>;
#[repr(transparent)]
#[derive(Copy, Clone, Pod, Zeroable, SwapEndian, Default)]
#[derive(Copy, Clone, Pod, Zeroable, SwapEndian, Default, Eq, PartialEq)]
pub struct TypesFlags(u16);
impl TypesFlags {
@ -96,10 +96,11 @@ impl Debug for TypesFlags {
}
#[repr(transparent)]
#[derive(PartialEq, Eq, Copy, Clone, Pod, Zeroable, SwapEndian, Hash)]
#[derive(PartialEq, Eq, Ord, PartialOrd, Copy, Clone, Pod, Zeroable, SwapEndian, Hash)]
pub struct VirtualPort(pub(crate) u8);
impl VirtualPort {
#[inline]
pub const fn get_stream_type(self) -> u8 {
(self.0 & 0xF0) >> 4
@ -141,7 +142,7 @@ impl Debug for VirtualPort {
}
#[repr(C)]
#[derive(Debug, Copy, Clone, Pod, Zeroable, SwapEndian)]
#[derive(Debug, Copy, Clone, Pod, Zeroable, SwapEndian, Eq, PartialEq)]
pub struct PRUDPHeader {
pub magic: [u8; 2],
pub version: u8,
@ -173,7 +174,7 @@ impl Default for PRUDPHeader{
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum PacketOption{
SupportedFunctions(u32),
ConnectionSignature([u8; 16]),
@ -236,7 +237,7 @@ impl PacketOption{
}
}
#[derive(Debug, Clone)]
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct PRUDPPacket {
pub header: PRUDPHeader,
pub packet_signature: [u8; 16],
@ -290,9 +291,12 @@ impl PRUDPPacket {
let packet_signature: [u8; 16] = reader.read_struct(IS_BIG_ENDIAN)?;
//let packet_signature: [u8; 16] = [0; 16];
assert_eq!(reader.stream_position().ok(), Some(14 + 16));
let mut packet_specific_buffer = vec![0u8; header.packet_specific_size as usize];
reader.read_exact(&mut packet_specific_buffer)?;
@ -372,6 +376,7 @@ impl PRUDPPacket {
types_and_flags: flags,
sequence_id: self.header.sequence_id,
substream_id: self.header.substream_id,
session_id: self.header.session_id,
..base.header
},
options,
@ -474,6 +479,8 @@ impl PRUDPPacket {
#[cfg(test)]
mod test {
use crate::prudp::packet::flags::{NEED_ACK, RELIABLE};
use crate::prudp::packet::types::DATA;
use super::{OptionId, PacketOption, PRUDPHeader, TypesFlags, VirtualPort};
#[test]
fn size_test() {
@ -523,4 +530,13 @@ mod test {
let header_data: [u8; 8] = bytes.try_into().unwrap();
}
#[test]
fn test_types_flags(){
let types = TypesFlags::default().types(DATA).flags(NEED_ACK | RELIABLE);
assert_ne!((types.0 >> 4) & NEED_ACK, 0);
assert_ne!((types.0 >> 4) & RELIABLE, 0);
assert_ne!((types.0 & 0xFF) as u8 & DATA, 0);
}
}

View file

@ -11,7 +11,7 @@ use once_cell::sync::Lazy;
use log::{error, info, trace};
use thiserror::Error;
use tokio::sync::RwLock;
use crate::prudp::socket::SocketData;
use crate::prudp::socket::{new_socket_pair, AnyInternalSocket, CryptoHandler, ExternalSocket};
use crate::prudp::packet::{PRUDPPacket, VirtualPort};
use crate::prudp::router::Error::VirtualPortTaken;
@ -22,10 +22,9 @@ static SERVER_DATAGRAMS: Lazy<u8> = Lazy::new(||{
});
pub struct Router {
endpoints: RwLock<[Option<Arc<SocketData>>; 16]>,
endpoints: RwLock<[Option<Arc<dyn AnyInternalSocket>>; 16]>,
running: AtomicBool,
socket: Arc<UdpSocket>,
//pub auth_module: Arc<dyn AuthModule>
_no_outside_construction: PhantomData<()>
}
#[derive(Debug, Error)]
@ -36,9 +35,6 @@ pub enum Error{
impl Router {
fn process_prudp_packet(&self, _packet: &PRUDPPacket){
}
async fn process_prudp_packets<'a>(self: Arc<Self>, _socket: Arc<UdpSocket>, addr: SocketAddrV4, udp_message: Vec<u8>){
let mut stream = Cursor::new(&udp_message);
@ -54,6 +50,7 @@ impl Router {
trace!("got valid prudp packet from someone({}): \n{:?}", addr, packet);
let connection = packet.source_sockaddr(addr);
let endpoints = self.endpoints.read().await;
@ -69,7 +66,9 @@ impl Router {
trace!("sending packet to endpoint");
endpoint.process_packet(connection, &packet).await;
tokio::spawn(async move {
endpoint.recieve_packet(connection, packet).await
});
}
}
@ -88,6 +87,7 @@ impl Router {
continue;
};
let current_msg = &msg_buffer[0..len];
tokio::spawn(self.clone().process_prudp_packets(socket.clone(), addr, current_msg.to_vec()));
@ -144,18 +144,22 @@ impl Router {
}
// returns Some(()) i
pub(crate) async fn add_socket(&self, socket: Arc<SocketData>) -> Result<(), Error>{
pub(crate) async fn add_socket<E: CryptoHandler>(&self, virtual_port: VirtualPort, encryption: E)
-> Result<ExternalSocket, Error>{
let mut endpoints = self.endpoints.write().await;
let idx = socket.get_virual_port().get_port_number() as usize;
let idx = virtual_port.get_port_number() as usize;
if endpoints[idx].is_none() {
endpoints[idx] = Some(socket);
} else {
// dont create the socket if we dont need to
if !endpoints[idx].is_none(){
return Err(VirtualPortTaken(idx as u8));
}
Ok(())
let (internal, external) = new_socket_pair(virtual_port, encryption, self.socket.clone());
endpoints[idx] = Some(internal);
Ok(external)
}
pub fn get_own_address(&self) -> SocketAddrV4{

View file

@ -4,10 +4,13 @@ use log::error;
use rc4::cipher::StreamCipherCoreWrapper;
use rc4::{KeyInit, Rc4, Rc4Core, StreamCipher};
use rc4::consts::U16;
use typenum::U5;
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
use crate::kerberos::{derive_key, TicketInternalData};
use crate::nex::account::Account;
use crate::prudp::socket::EncryptionPair;
use crate::prudp::packet::PRUDPPacket;
use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance, EncryptionPair};
use crate::prudp::unsecure::UnsecureInstance;
use crate::rmc::structures::RmcSerialize;
pub fn read_secure_connection_data(data: &[u8], act: &Account) -> Option<([u8; 32], u32, u32)>{
@ -73,12 +76,12 @@ pub fn read_secure_connection_data(data: &[u8], act: &Account) -> Option<([u8; 3
type Rc4U32 = StreamCipherCoreWrapper<Rc4Core<U32>>;
pub fn generate_secure_encryption_pairs(mut session_key: [u8; 32], count: u8) -> Vec<EncryptionPair>{
pub fn generate_secure_encryption_pairs(mut session_key: [u8; 32], count: u8) -> Vec<EncryptionPair<Rc4<U32>>>{
let mut vec = Vec::with_capacity(count as usize);
vec.push(EncryptionPair{
send: Box::new(Rc4U32::new_from_slice(&session_key).expect("unable to create rc4")),
recv: Box::new(Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"))
send: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"),
recv: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4")
});
for _ in 1..=count{
@ -91,10 +94,99 @@ pub fn generate_secure_encryption_pairs(mut session_key: [u8; 32], count: u8) ->
}
vec.push(EncryptionPair{
send: Box::new(Rc4U32::new_from_slice(&session_key).expect("unable to create rc4")),
recv: Box::new(Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"))
send: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"),
recv: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4")
});
}
vec
}
pub struct Secure(pub &'static str, pub &'static Account);
pub struct SecureInstance {
access_key: &'static str,
session_key: [u8; 32],
streams: Vec<EncryptionPair<Rc4<U32>>>,
self_signature: [u8; 16],
remote_signature: [u8; 16],
pid: u32,
}
impl CryptoHandler for Secure {
type CryptoConnectionInstance = SecureInstance;
fn instantiate(
&self,
remote_signature: [u8; 16],
self_signature: [u8; 16],
payload: &[u8],
substream_count: u8,
) -> Option<(Vec<u8>, Self::CryptoConnectionInstance)> {
let (session_key, pid, check_value) = read_secure_connection_data(payload, &self.1)?;
let check_value_response = check_value + 1;
let data = bytemuck::bytes_of(&check_value_response);
let mut response = Vec::new();
data.serialize(&mut response).ok()?;
let encryption_pairs = generate_secure_encryption_pairs(session_key, substream_count);
Some((
response,
SecureInstance {
pid,
streams: encryption_pairs,
session_key,
access_key: self.0,
remote_signature,
self_signature,
},
))
}
fn sign_pre_handshake(&self, packet: &mut PRUDPPacket) {
packet.set_sizes();
packet.calculate_and_assign_signature(self.0, None, None);
}
}
impl CryptoHandlerConnectionInstance for SecureInstance {
type Encryption = Rc4<U5>;
fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]) {
if let Some(crypt_pair) = self.streams.get_mut(substream as usize){
crypt_pair.recv.apply_keystream(data);
}
}
fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]) {
if let Some(crypt_pair) = self.streams.get_mut(substream as usize){
crypt_pair.send.apply_keystream(data);
}
}
fn get_user_id(&self) -> u32 {
self.pid
}
fn sign_connect(&self, packet: &mut PRUDPPacket) {
packet.set_sizes();
packet.calculate_and_assign_signature(self.access_key, None, Some(self.self_signature));
}
fn sign_packet(&self, packet: &mut PRUDPPacket) {
packet.set_sizes();
packet.calculate_and_assign_signature(self.access_key, Some(self.session_key), Some(self.self_signature));
}
fn verify_packet(&self, packet: &PRUDPPacket) -> bool {
true
}
}

View file

@ -5,18 +5,28 @@ use crate::prudp::packet::VirtualPort;
type Md5Hmac = Hmac<md5::Md5>;
#[derive(Eq, PartialEq, Hash, Debug, Copy, Clone)]
#[derive(Eq, PartialEq, Hash, Debug, Copy, Clone, Ord, PartialOrd)]
pub struct PRUDPSockAddr{
pub regular_socket_addr: SocketAddrV4,
pub virtual_port: VirtualPort
}
impl PRUDPSockAddr{
pub fn calculate_connection_signature(&self) -> [u8; 16] {
pub fn new(regular_socket_addr: SocketAddrV4, virtual_port: VirtualPort) -> Self{
Self{
regular_socket_addr,
virtual_port
}
}
pub(super) fn calculate_connection_signature(&self) -> [u8; 16] {
let mut hmac = Md5Hmac::new_from_slice(&[0; 16]).expect("fuck");
let mut data = self.regular_socket_addr.ip().octets().to_vec();
data.extend_from_slice(&self.regular_socket_addr.port().to_be_bytes());
//data.extend_from_slice(&self.regular_socket_addr.port().to_be_bytes());
hmac.write_all(&data).expect("figuring this out was complete ass");
let result: [u8; 16] = hmac.finalize().into_bytes()[0..16].try_into().expect("fuck");

File diff suppressed because it is too large Load diff

84
src/prudp/unsecure.rs Normal file
View file

@ -0,0 +1,84 @@
use once_cell::sync::Lazy;
use rc4::{Key, KeyInit, Rc4, StreamCipher};
use typenum::U5;
use crate::prudp::packet::PRUDPPacket;
use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance, EncryptionPair};
pub struct Unsecure(pub &'static str);
pub struct UnsecureInstance {
key: &'static str,
streams: Vec<EncryptionPair<Rc4<U5>>>,
self_signature: [u8; 16],
remote_signature: [u8; 16],
}
// my hand was forced to use lazy so that we can guarantee this code
// only runs once and so that i can put it here as a "constant" (for performance and readability)
// since for some reason rust crypto doesn't have any const time key initialization
static DEFAULT_KEY: Lazy<Key<U5>> = Lazy::new(|| Key::from(*b"CD&ML"));
impl CryptoHandler for Unsecure {
type CryptoConnectionInstance = UnsecureInstance;
fn instantiate(
&self,
remote_signature: [u8; 16],
self_signature: [u8; 16],
_: &[u8],
substream_count: u8,
) -> Option<(Vec<u8>, Self::CryptoConnectionInstance)> {
Some((
Vec::new(),
UnsecureInstance {
streams: (0..substream_count)
.map(|_| EncryptionPair::init_both(|| Rc4::new(&DEFAULT_KEY)))
.collect(),
key: self.0,
remote_signature,
self_signature,
},
))
}
fn sign_pre_handshake(&self, packet: &mut PRUDPPacket) {
packet.set_sizes();
packet.calculate_and_assign_signature(self.0, None, None);
}
}
impl CryptoHandlerConnectionInstance for UnsecureInstance {
type Encryption = Rc4<U5>;
fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]) {
if let Some(crypt_pair) = self.streams.get_mut(substream as usize){
crypt_pair.recv.apply_keystream(data);
}
}
fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]) {
if let Some(crypt_pair) = self.streams.get_mut(substream as usize){
crypt_pair.send.apply_keystream(data);
}
}
fn get_user_id(&self) -> u32 {
0
}
fn sign_connect(&self, packet: &mut PRUDPPacket) {
packet.set_sizes();
packet.calculate_and_assign_signature(self.key, None, Some(self.self_signature));
}
fn sign_packet(&self, packet: &mut PRUDPPacket) {
packet.set_sizes();
packet.calculate_and_assign_signature(self.key, None, Some(self.self_signature));
}
fn verify_packet(&self, packet: &PRUDPPacket) -> bool {
true
}
}

23
src/result.rs Normal file
View file

@ -0,0 +1,23 @@
use std::error::Error;
use log::error;
pub trait ResultExtension{
type Output;
fn display_err_or_some(self) -> Option<Self::Output>;
}
impl<T, U: Error> ResultExtension for Result<T, U>{
type Output = T;
fn display_err_or_some(self) -> Option<Self::Output> {
match self{
Ok(v) => Some(v),
Err(e) => {
error!("{}", e);
None
}
}
}
}

View file

@ -5,7 +5,7 @@ use log::error;
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
use crate::rmc::response::{ErrorCode, RMCResponseResult};
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RMCMessage{
pub protocol_id: u16,
pub call_id: u32,
@ -60,13 +60,15 @@ impl RMCMessage{
output.write_all(bytes_of(&size)).expect("unable to write size");
let proto_id = self.protocol_id as u8;
let proto_id = self.protocol_id as u8 | 0x80;
output.write_all(bytes_of(&proto_id)).expect("unable to write size");
output.write_all(bytes_of(&self.call_id)).expect("unable to write size");
output.write_all(bytes_of(&self.method_id)).expect("unable to write size");
output.write_all(&self.rest_of_data).expect("unable to write data");
output
}

View file

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

47
src/rmc/protocols/auth.rs Normal file
View file

@ -0,0 +1,47 @@
use crate::rmc::response::ErrorCode;
use crate::rmc::structures::any::Any;
use crate::rmc::structures::connection_data::ConnectionData;
use crate::rmc::structures::qresult::QResult;
use macros::{method_id, rmc_proto};
/// This is the representation for `Ticket Granting`(for details see the
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
#[rmc_proto(10)]
pub trait Auth {
/// representation of the `Login` method(for details see the
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
#[method_id(1)]
async fn login(&self, name: String) -> Result<(), ErrorCode>;
/// representation of the `LoginEx` method(for details see the
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
#[method_id(2)]
async fn login_ex(
&self,
name: String,
extra_data: Any,
) -> Result<(QResult, u32, Vec<u8>, ConnectionData, String), ErrorCode>;
/// representation of the `RequestTicket` method(for details see the
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
#[method_id(3)]
async fn request_ticket(
&self,
source_pid: u32,
destination_pid: u32,
) -> Result<(QResult, Vec<u8>), ErrorCode>;
/// representation of the `GetPID` method(for details see the
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
#[method_id(4)]
async fn get_pid(&self, username: String) -> Result<u32, ErrorCode>;
/// representation of the `LoginWithContext` method(for details see the
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
#[method_id(5)]
async fn get_name(&self, pid: u32) -> Result<String, ErrorCode>;
// `LoginWithContext` is left out here because we don't need it right now and versioning still
// needs to be figured out
}

303
src/rmc/protocols/mod.rs Normal file
View file

@ -0,0 +1,303 @@
#![allow(async_fn_in_trait)]
pub mod auth;
pub mod secure;
use crate::prudp::socket::{ExternalConnection, SendingConnection};
use crate::rmc::message::RMCMessage;
use crate::rmc::protocols::RemoteCallError::ConnectionBroke;
use crate::rmc::response::{ErrorCode, RMCResponse, RMCResponseResult};
use crate::rmc::structures;
use crate::rmc::structures::connection_data::ConnectionData;
use crate::rmc::structures::matchmake::AutoMatchmakeParam;
use crate::rmc::structures::{Error, RmcSerialize};
use async_trait::async_trait;
use chrono::TimeDelta;
use log::{error, info};
use macros::method_id;
use macros::{rmc_proto, rmc_struct};
use paste::paste;
use std::collections::HashMap;
use std::io::Cursor;
use std::ops::{Add, Deref};
use std::sync::{Arc, Condvar};
use std::time::Duration;
use thiserror::Error;
use tokio::sync::{Mutex, Notify};
use tokio::time::{sleep_until, Instant};
use crate::result::ResultExtension;
#[derive(Error, Debug)]
pub enum RemoteCallError {
#[error("Call to remote timed out whilest waiting on response.")]
Timeout,
#[error("A server side rmc error occurred: {0:?}")]
ServerError(ErrorCode),
#[error("Connection broke")]
ConnectionBroke,
#[error("Error reading response data: {0}")]
InvalidResponse(#[from] structures::Error),
}
pub struct RmcConnection(pub SendingConnection, 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(())
}
}
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: &SendingConnection,
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!{
pub trait [<Local $name>]: std::any::Any $( + [<Raw $protocol>] + $protocol)* {
async fn rmc_call(&self, remote_response_connection: &crate::prudp::socket::SendingConnection, 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>](crate::rmc::protocols::RmcConnection);
impl crate::rmc::protocols::RemoteInstantiatable for [<Remote $name>]{
fn new(conn: crate::rmc::protocols::RmcConnection) -> Self{
Self(conn)
}
}
impl crate::rmc::protocols::HasRmcConnection for [<Remote $name>]{
fn get_connection(&self) -> &crate::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: &crate::prudp::socket::SendingConnection,
protocol_id: u16,
method_id: u32,
call_id: u32,
rest: Vec<u8>,
) {
//todo: maybe reply with not implemented(?)
}
}
pub trait RemoteInstantiatable{
fn new(conn: RmcConnection) -> Self;
}
pub struct OnlyRemote<T: RemoteInstantiatable>(T);
impl<T: RemoteInstantiatable> Deref for OnlyRemote<T>{
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: RemoteInstantiatable> OnlyRemote<T>{
pub fn new(conn: RmcConnection) -> Self{
Self(T::new(conn))
}
}
impl<T: RemoteInstantiatable> RmcCallable for OnlyRemote<T>{
fn rmc_call(&self, responder: &SendingConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec<u8>) -> impl std::future::Future<Output = ()> + Send {
async{}
}
}
async fn handle_incoming<T: RmcCallable + Send + Sync + 'static>(
mut connection: ExternalConnection,
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
};
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!("got rmc request, handeling it now...");
remote.rmc_call(&sending_conn, protocol_id, method_id, call_id, rest_of_data).await;
}
}
}
pub fn new_rmc_gateway_connection<T: RmcCallable + Sync + Send + 'static,F>(conn: ExternalConnection, create_internal: F) -> Arc<T>
where
F: FnOnce(RmcConnection) -> 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 exposed_object = (create_internal)(rmc_conn);
let exposed_object = Arc::new(exposed_object);
{
let exposed_object = exposed_object.clone();
tokio::spawn(async move {
handle_incoming(
conn,
exposed_object,
notify,
incoming
).await;
});
}
exposed_object
}

View file

@ -0,0 +1,12 @@
use macros::{method_id, rmc_proto};
use crate::prudp::station_url::StationUrl;
use crate::rmc::response::ErrorCode;
use crate::rmc::structures::any::Any;
use crate::rmc::structures::connection_data::ConnectionData;
use crate::rmc::structures::qresult::QResult;
#[rmc_proto(11)]
pub trait Auth {
#[method_id(1)]
async fn register(&self, station_urls: Vec<String>) -> Result<(QResult, u32, String), ErrorCode>;
}

View file

@ -1,44 +1,114 @@
use std::io;
use std::io::{Write};
use std::io::{Read, Seek, Write};
use std::mem::transmute;
use bytemuck::bytes_of;
use log::error;
use v_byte_macros::EnumTryInto;
use crate::endianness::{ReadExtensions, IS_BIG_ENDIAN};
use crate::prudp::packet::{PRUDPPacket};
use crate::prudp::packet::flags::{NEED_ACK, RELIABLE};
use crate::prudp::packet::PacketOption::FragmentId;
use crate::prudp::packet::types::DATA;
use crate::prudp::socket::{ConnectionData, SocketData};
use crate::prudp::socket::{ExternalConnection, SendingConnection};
use crate::rmc::response::ErrorCode::Core_Exception;
use crate::rmc::structures::qresult::ERROR_MASK;
use crate::rmc::structures::RmcSerialize;
use crate::web::DirectionalData::{Incoming, Outgoing};
use crate::web::WEB_DATA;
pub enum RMCResponseResult {
Success{
Success {
call_id: u32,
method_id: u32,
data: Vec<u8>,
},
Error{
Error {
error_code: ErrorCode,
call_id: u32,
}
},
}
pub struct RMCResponse {
pub protocol_id: u8,
pub response_result: RMCResponseResult
pub response_result: RMCResponseResult,
}
impl RMCResponse {
pub fn to_data(self) -> Vec<u8>{
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(e) => {
error!("invalid error code {:#010x}", error_code);
Core_Exception
}
}
},
call_id,
}
};
Ok(Self{
protocol_id,
response_result
})
}
pub fn get_call_id(&self) -> u32{
match &self.response_result{
RMCResponseResult::Success { call_id, ..} => *call_id,
RMCResponseResult::Error { call_id, .. } => *call_id
}
}
pub fn to_data(self) -> Vec<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{
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,
RMCResponseResult::Error { .. } => 4 + 4,
};
let mut data_out = Vec::with_capacity(size + 4);
@ -48,7 +118,7 @@ pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Re
data_out.write_all(bytes_of(&u32_size))?;
data_out.push(protocol_id);
match response{
match response {
RMCResponseResult::Success {
call_id,
method_id,
@ -59,7 +129,7 @@ pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Re
let ored_method_id = method_id | 0x8000;
data_out.write_all(bytes_of(&ored_method_id))?;
data_out.write_all(&data)?;
},
}
RMCResponseResult::Error {
call_id,
error_code
@ -76,38 +146,46 @@ pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Re
Ok(data_out)
}
pub async fn send_response(original_packet: &PRUDPPacket, socket: &SocketData, connection: &mut ConnectionData, rmcresponse: RMCResponse){
let ConnectionData{
active_connection_data,
..
} = connection;
let Some(active_connection) = active_connection_data else {
return;
pub async fn send_result(
connection: &SendingConnection,
result: Result<Vec<u8>, ErrorCode>,
protocol_id: u8,
method_id: u32,
call_id: u32,
) {
println!("{}", hex::encode(result.clone().unwrap()));
let response_result = match result {
Ok(v) => RMCResponseResult::Success {
call_id,
method_id,
data: v
},
Err(e) =>
RMCResponseResult::Error {
call_id,
error_code: e.into()
}
};
let mut packet = original_packet.base_response_packet();
let response = RMCResponse{
response_result,
protocol_id
};
packet.header.types_and_flags.set_types(DATA);
packet.header.types_and_flags.set_flag((original_packet.header.types_and_flags.get_flags() & RELIABLE) | NEED_ACK);
packet.header.session_id = active_connection.server_session_id;
packet.header.substream_id = 0;
packet.options.push(FragmentId(0));
packet.payload = rmcresponse.to_data();
//tokio::time::sleep(Duration::from_millis(500)).await;
connection.finish_and_send_packet_to(socket, packet).await;
send_response(connection, response).await
}
pub async fn send_response(connection: &SendingConnection, rmcresponse: RMCResponse) {
connection.send(rmcresponse.to_data()).await;
}
//taken from kinnays error list directly
#[allow(nonstandard_style)]
#[repr(u32)]
#[derive(Debug, EnumTryInto, Clone, Copy)]
pub enum ErrorCode {
Core_Unknown = 0x00010001,
Core_NotImplemented = 0x00010002,
@ -379,25 +457,25 @@ pub enum ErrorCode {
Custom_Unknown = 0x00740001,
Ess_Unknown = 0x00750001,
Ess_GameSessionError = 0x00750002,
Ess_GameSessionMaintenance = 0x00750003
Ess_GameSessionMaintenance = 0x00750003,
}
impl Into<u32> for ErrorCode {
impl Into<u32> for ErrorCode {
fn into(self) -> u32 {
unsafe{ transmute(self) }
unsafe { transmute(self) }
}
}
#[cfg(test)]
mod test{
mod test {
use hmac::digest::consts::U5;
use hmac::digest::KeyInit;
use rc4::{Rc4, StreamCipher};
use crate::rmc::response::ErrorCode;
#[test]
fn test(){
let mut data_orig = [0,1,2,3,4,5,6,7,8,9,69,4,20];
fn test() {
let mut data_orig = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 69, 4, 20];
let mut data = data_orig;
let mut rc4: Rc4<U5> =
@ -413,11 +491,10 @@ mod test{
rc4.apply_keystream(&mut data);
assert_eq!(data_orig, data);
}
#[test]
fn test_enum_equivilance(){
fn test_enum_equivilance() {
let val: u32 = ErrorCode::Core_Unknown.into();
assert_eq!(val, 0x00010001)
}

View file

@ -2,15 +2,24 @@ use std::io::{Read, Write};
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
use super::{Result, RmcSerialize};
#[derive(Debug)]
#[derive(Debug, Default)]
pub struct Any{
pub name: String,
pub data: Vec<u8>
}
impl RmcSerialize for Any{
fn serialize(&self, _writer: &mut dyn Write) -> Result<()> {
todo!()
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
self.name.serialize(writer)?;
let u32_len = self.data.len() as u32;
u32_len.serialize(writer)?;
u32_len.serialize(writer)?;
self.data.serialize(writer)?;
Ok(())
}
fn deserialize(mut reader: &mut dyn Read) -> Result<Self> {
let name = String::deserialize(reader)?;

View file

@ -1,6 +1,8 @@
use std::io::{Read, Write};
use crate::rmc::structures::RmcSerialize;
impl<'a> RmcSerialize for &'a [u8]{
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
let u32_size = self.len() as u32;

View file

@ -1,29 +1,15 @@
use std::io::{Read, Write};
use bytemuck::bytes_of;
use macros::RmcSerialize;
use crate::kerberos::KerberosDateTime;
use crate::rmc::structures::{rmc_struct, RmcSerialize};
pub struct ConnectionData<'a>{
pub station_url: &'a str,
#[derive(Debug, RmcSerialize)]
#[rmc_struct(1)]
pub struct ConnectionData{
pub station_url: String,
pub special_protocols: Vec<u8>,
pub special_station_url: &'a str,
pub special_station_url: String,
pub date_time: KerberosDateTime
}
impl<'a> RmcSerialize for ConnectionData<'a>{
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
rmc_struct::write_struct(writer, 1, |v|{
self.station_url.serialize(v).expect("unable to write station url");
self.special_protocols.serialize(v).expect("unable to write special protocols");
self.special_station_url.serialize(v).expect("unable to write special station url");
v.write_all(bytes_of(&self.date_time)).expect("unable to write date time");
Ok(())
})
}
fn deserialize(_reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
todo!()
}
}

View file

@ -4,6 +4,9 @@ use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
use crate::rmc::structures::RmcSerialize;
// this is also for implementing `Buffer` this is tecnically not the same as its handled internaly
// probably but as it has the same mapping it doesn't matter and simplifies things
impl<T: RmcSerialize> RmcSerialize for Vec<T>{
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
let u32_len = self.len() as u32;

View file

@ -3,7 +3,7 @@ use crate::kerberos::KerberosDateTime;
use crate::rmc::structures::variant::Variant;
// rmc structure
#[derive(RmcSerialize, Debug, Clone)]
#[derive(RmcSerialize, Debug, Clone, Default)]
#[rmc_struct(0)]
pub struct Gathering {
pub self_gid: u32,
@ -19,7 +19,7 @@ pub struct Gathering {
}
// rmc structure
#[derive(RmcSerialize, Debug, Clone)]
#[derive(RmcSerialize, Debug, Clone, Default)]
#[rmc_struct(0)]
pub struct MatchmakeParam {
pub params: Vec<(String, Variant)>,
@ -27,7 +27,7 @@ pub struct MatchmakeParam {
// rmc structure
#[derive(RmcSerialize, Debug, Clone)]
#[derive(RmcSerialize, Debug, Clone, Default)]
#[rmc_struct(3)]
pub struct MatchmakeSession {
//inherits from
@ -81,4 +81,16 @@ pub struct AutoMatchmakeParam {
pub participation_count: u16,
pub search_criteria: Vec<MatchmakeSessionSearchCriteria>,
pub target_gids: Vec<u32>,
}
}
#[derive(RmcSerialize, Debug, Clone)]
#[rmc_struct(0)]
pub struct CreateMatchmakeSessionParam {
pub matchmake_session: MatchmakeSession,
pub additional_participants: Vec<u32>,
pub gid_for_participation_check: u32,
pub create_matchmake_session_option: u32,
pub join_message: String,
pub participation_count: u16,
}

View file

@ -17,7 +17,7 @@ pub enum Error{
VersionMismatch(u8),
}
pub(crate) type Result<T> = std::result::Result<T, Error>;
pub type Result<T> = std::result::Result<T, Error>;
pub mod string;
pub mod any;
@ -30,8 +30,18 @@ pub mod qbuffer;
pub mod primitives;
pub mod matchmake;
pub mod variant;
pub mod ranking;
pub trait RmcSerialize: Sized{
fn serialize(&self, writer: &mut dyn Write) -> Result<()>;
fn deserialize(reader: &mut dyn Read) -> Result<Self>;
}
impl RmcSerialize for (){
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
Ok(())
}
fn deserialize(reader: &mut dyn Read) -> Result<Self> {
Ok(())
}
}

View file

@ -91,4 +91,112 @@ impl<T: RmcSerialize, U: RmcSerialize> RmcSerialize for (T, U){
Ok((first, second))
}
}
impl<T: RmcSerialize, U: RmcSerialize, V: RmcSerialize> RmcSerialize for (T, U, V){
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
self.0.serialize(writer)?;
self.1.serialize(writer)?;
self.2.serialize(writer)?;
Ok(())
}
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
let first = T::deserialize(reader)?;
let second = U::deserialize(reader)?;
let third = V::deserialize(reader)?;
Ok((first, second, third))
}
}
impl<T: RmcSerialize, U: RmcSerialize, V: RmcSerialize, W: RmcSerialize> RmcSerialize for (T, U, V, W){
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
self.0.serialize(writer)?;
self.1.serialize(writer)?;
self.2.serialize(writer)?;
self.3.serialize(writer)?;
Ok(())
}
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
let first = T::deserialize(reader)?;
let second = U::deserialize(reader)?;
let third = V::deserialize(reader)?;
let fourth = W::deserialize(reader)?;
Ok((first, second, third, fourth))
}
}
impl<T: RmcSerialize, U: RmcSerialize, V: RmcSerialize, W: RmcSerialize, X: RmcSerialize> RmcSerialize for (T, U, V, W, X){
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
self.0.serialize(writer)?;
self.1.serialize(writer)?;
self.2.serialize(writer)?;
self.3.serialize(writer)?;
self.4.serialize(writer)?;
Ok(())
}
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
let first = T::deserialize(reader)?;
let second = U::deserialize(reader)?;
let third = V::deserialize(reader)?;
let fourth = W::deserialize(reader)?;
let fifth = X::deserialize(reader)?;
Ok((first, second, third, fourth, fifth))
}
}
impl<T: RmcSerialize, U: RmcSerialize, V: RmcSerialize, W: RmcSerialize, X: RmcSerialize, Y: RmcSerialize> RmcSerialize for (T, U, V, W, X, Y){
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
self.0.serialize(writer)?;
self.1.serialize(writer)?;
self.2.serialize(writer)?;
self.3.serialize(writer)?;
self.4.serialize(writer)?;
self.5.serialize(writer)?;
Ok(())
}
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
let first = T::deserialize(reader)?;
let second = U::deserialize(reader)?;
let third = V::deserialize(reader)?;
let fourth = W::deserialize(reader)?;
let fifth = X::deserialize(reader)?;
let sixth = Y::deserialize(reader)?;
Ok((first, second, third, fourth, fifth, sixth))
}
}
impl<T: RmcSerialize, U: RmcSerialize, V: RmcSerialize, W: RmcSerialize, X: RmcSerialize, Y: RmcSerialize, Z: RmcSerialize> RmcSerialize for (T, U, V, W, X, Y, Z){
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
self.0.serialize(writer)?;
self.1.serialize(writer)?;
self.2.serialize(writer)?;
self.3.serialize(writer)?;
self.4.serialize(writer)?;
self.5.serialize(writer)?;
self.6.serialize(writer)?;
Ok(())
}
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> {
let first = T::deserialize(reader)?;
let second = U::deserialize(reader)?;
let third = V::deserialize(reader)?;
let fourth = W::deserialize(reader)?;
let fifth = X::deserialize(reader)?;
let sixth = Y::deserialize(reader)?;
let seventh = Z::deserialize(reader)?;
Ok((first, second, third, fourth, fifth, sixth, seventh))
}
}

View file

@ -1,12 +1,30 @@
use std::io::Read;
use std::io::{Read, Write};
use bytemuck::bytes_of;
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
use crate::rmc::structures::Result;
pub fn read(reader: &mut impl Read) -> Result<Vec<u8>>{
let size: u16 = reader.read_struct(IS_BIG_ENDIAN)?;
use crate::rmc::structures::{Result, RmcSerialize};
use crate::rmc::structures::qresult::QResult;
let mut vec = vec![0; size as usize];
reader.read_exact(&mut vec)?;
#[derive(Debug)]
pub struct QBuffer(pub Vec<u8>);
Ok(vec)
impl RmcSerialize for QBuffer{
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
let len_u16 = self.0.len() as u16;
writer.write(bytes_of(&len_u16))?;
writer.write(&self.0)?;
Ok(())
}
fn deserialize(mut reader: &mut dyn Read) -> Result<Self> {
let size: u16 = reader.read_struct(IS_BIG_ENDIAN)?;
let mut vec = vec![0; size as usize];
reader.read_exact(&mut vec)?;
Ok(Self(vec))
}
}

View file

@ -7,7 +7,7 @@ use crate::rmc::structures::{RmcSerialize, Result};
pub const ERROR_MASK: u32 = 1 << 31;
#[derive(Pod, Zeroable, Copy, Clone, SwapEndian)]
#[derive(Pod, Zeroable, Copy, Clone, SwapEndian, Debug)]
#[repr(transparent)]
pub struct QResult(u32);

View file

@ -0,0 +1,69 @@
use bytemuck::{Pod, Zeroable};
use macros::RmcSerialize;
use crate::rmc::structures::qbuffer::QBuffer;
#[derive(RmcSerialize, Debug)]
#[rmc_struct(0)]
struct UploadCompetitionData{
winning_team/*?*/: u32,
splatfest_id/*?*/: u32,
unk_2/*?*/: u32,
unk_3: u32,
team_id_1: u8,
team_id_2: u8,
unk_5: u32,
player_data/*?*/: QBuffer,
}
#[derive(Copy, Clone, Pod, Zeroable)]
#[repr(C)]
struct UserData{
name: [u16; 0x10],
}
#[cfg(test)]
mod test{
use std::io::Cursor;
use bytemuck::from_bytes;
use tokio::io::AsyncReadExt;
use crate::rmc::structures::ranking::{UploadCompetitionData, UserData};
use crate::rmc::structures::RmcSerialize;
#[test]
fn test() {
let data: [u8; 0xBD] = [
0x00, 0xB8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00,
0x00, 0x1F, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x49, 0x00,
0x7A, 0x00, 0x7A, 0x00, 0x79, 0x00, 0x53, 0x00, 0x50, 0x00, 0x46, 0x00, 0x4E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0xF2, 0x00, 0x00, 0x00,
0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1F, 0x5E, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x90, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0A, 0x00, 0x00, 0x14, 0x87, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00,
];
let mut cursor = Cursor::new(data);
let data = UploadCompetitionData::deserialize(&mut cursor).expect("unable to deserialize data");
let user_data: &UserData = from_bytes(&data.player_data.0[..size_of::<UserData>()]);
let pos = user_data.name.iter()
.position(|v| *v == 0x0000)
.unwrap_or(0x10);
let mut name = user_data.name[0..pos].to_vec();
name.iter_mut().for_each(|v| *v = v.swap_bytes());
let name = String::from_utf16(&name).expect("unable to get name");
println!("{:?}", name);
assert!(u8::deserialize(&mut cursor).is_err())
}
}

View file

@ -17,7 +17,7 @@ pub fn write_struct(writer: &mut dyn Write, version: u8, pred: impl FnOnce(&mut
(pred)(&mut scratch_space)?;
let u32_size= scratch_space.len() as u32;
let u32_size = scratch_space.len() as u32;
writer.write_all(bytes_of(&u32_size))?;
writer.write_all(&scratch_space)?;

View file

@ -35,4 +35,5 @@ impl RmcSerialize for &str{
Ok(())
}
}
}

View file

@ -3,8 +3,9 @@ use crate::kerberos::KerberosDateTime;
use crate::rmc::structures;
use crate::rmc::structures::RmcSerialize;
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Default)]
pub enum Variant{
#[default]
None,
SInt64(i64),
Double(f64),

97
src/versions.rs Normal file
View file

@ -0,0 +1,97 @@
use std::marker::PhantomData;
use std::ops::{BitAnd, BitOr};
use typenum::{Cmp, IsEqual, IsLess, IsLessOrEqual, Unsigned, U1, U2, U3};
/// This trait represents a version at compile time
trait Version{
type Major: Unsigned;
type Minor: Unsigned;
}
/// This struct contains nothing and is used to represent specific versions as an instance of
/// [`Version`]. It is instances as `Ver<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> where {
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;

37
src/web/mod.rs Normal file
View file

@ -0,0 +1,37 @@
use std::net::SocketAddrV4;
use once_cell::sync::Lazy;
use rocket::{get, routes, Rocket};
use rocket::serde::json::Json;
use tokio::task::JoinHandle;
use serde::Serialize;
use tokio::sync::Mutex;
#[get("/")]
async fn server_data() -> Json<WebData> {
Json(WEB_DATA.lock().await.clone())
}
pub async fn start_web() -> JoinHandle<()>{
tokio::spawn(async{
rocket::build()
.mount("/",routes![server_data])
.launch().await
.expect("unable to start webserver");
})
}
#[derive(Serialize, Clone)]
pub enum DirectionalData{
Incoming(String),
Outgoing(String)
}
#[derive(Serialize, Default, Clone)]
pub struct WebData{
pub data: Vec<(SocketAddrV4, DirectionalData)>
}
pub static WEB_DATA: Lazy<Mutex<WebData>> = Lazy::new(|| Mutex::new(
WebData{
data: Vec::new(),
}
));