Merge branch 'feat/gatherings' into 'main'
Core Rewrite See merge request perditum/rnex-splatoon!10
This commit is contained in:
commit
c9fa7f48cd
65 changed files with 4011 additions and 939 deletions
30
.github/workflows/build.yml
vendored
30
.github/workflows/build.yml
vendored
|
|
@ -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
|
||||
14
.github/workflows/stale.yml
vendored
14
.github/workflows/stale.yml
vendored
|
|
@ -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
943
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -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
4
macros/Cargo.lock
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
291
macros/src/protos.rs
Normal 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);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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)>;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
310
src/main.rs
310
src/main.rs
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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};
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
136
src/nex-implementation/matchmake_common/mod.rs
Normal file
136
src/nex-implementation/matchmake_common/mod.rs
Normal 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
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
|
|
@ -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
|
||||
}
|
||||
10
src/nex-implementation/nat_traversal/mod.rs
Normal file
10
src/nex-implementation/nat_traversal/mod.rs
Normal 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
|
||||
}
|
||||
}
|
||||
159
src/nex-implementation/notification/mod.rs
Normal file
159
src/nex-implementation/notification/mod.rs
Normal 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);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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};
|
||||
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
@ -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
167
src/nex/auth_handler.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +1,3 @@
|
|||
pub mod account;
|
||||
pub mod account;
|
||||
pub mod auth_handler;
|
||||
pub mod user;
|
||||
45
src/nex/user.rs
Normal file
45
src/nex/user.rs
Normal 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()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
|
|
|
|||
1117
src/prudp/socket.rs
1117
src/prudp/socket.rs
File diff suppressed because it is too large
Load diff
84
src/prudp/unsecure.rs
Normal file
84
src/prudp/unsecure.rs
Normal 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
23
src/result.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
pub mod message;
|
||||
pub mod structures;
|
||||
pub mod response;
|
||||
pub mod protocols;
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
47
src/rmc/protocols/auth.rs
Normal file
47
src/rmc/protocols/auth.rs
Normal 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
303
src/rmc/protocols/mod.rs
Normal 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
|
||||
}
|
||||
12
src/rmc/protocols/secure.rs
Normal file
12
src/rmc/protocols/secure.rs
Normal 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>;
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
69
src/rmc/structures/ranking.rs
Normal file
69
src/rmc/structures/ranking.rs
Normal 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())
|
||||
}
|
||||
}
|
||||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -35,4 +35,5 @@ impl RmcSerialize for &str{
|
|||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
97
src/versions.rs
Normal 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
37
src/web/mod.rs
Normal 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(),
|
||||
}
|
||||
));
|
||||
Loading…
Add table
Add a link
Reference in a new issue