V0 #2

Closed
RusticMaple wants to merge 58 commits from v0 into main
140 changed files with 6157 additions and 5913 deletions

17
.ci-scripts/make-edition.sh Executable file
View file

@ -0,0 +1,17 @@
#!/usr/bin/env bash
export EDITION=$1
export BA="--build-arg EDITION=$1"
# podman build $BA -t "$CI_REGISTRY_IMAGE/$EDITION/dev-container:latest" --target=dev-container .
# podman push "$CI_REGISTRY_IMAGE/$EDITION/dev-container:latest"
podman build $BA -t "$CI_REGISTRY_IMAGE/$EDITION/node-holder:$CI_COMMIT_SHORT_SHA" --target=node-holder .
podman build $BA -t "$CI_REGISTRY_IMAGE/$EDITION/proxy-secure:$CI_COMMIT_SHORT_SHA" --target=proxy-secure .
podman build $BA -t "$CI_REGISTRY_IMAGE/$EDITION/proxy-insecure:$CI_COMMIT_SHORT_SHA" --target=proxy-insecure .
podman build $BA -t "$CI_REGISTRY_IMAGE/$EDITION/backend-auth:$CI_COMMIT_SHORT_SHA" --target=backend-auth .
podman build $BA -t "$CI_REGISTRY_IMAGE/$EDITION/backend-secure:$CI_COMMIT_SHORT_SHA" --target=backend-secure .
podman push "$CI_REGISTRY_IMAGE/$EDITION/node-holder:$CI_COMMIT_SHORT_SHA"
podman push "$CI_REGISTRY_IMAGE/$EDITION/proxy-secure:$CI_COMMIT_SHORT_SHA"
podman push "$CI_REGISTRY_IMAGE/$EDITION/proxy-insecure:$CI_COMMIT_SHORT_SHA"
podman push "$CI_REGISTRY_IMAGE/$EDITION/backend-auth:$CI_COMMIT_SHORT_SHA"
podman push "$CI_REGISTRY_IMAGE/$EDITION/backend-secure:$CI_COMMIT_SHORT_SHA"

3
.devcontainer.json Normal file
View file

@ -0,0 +1,3 @@
{
"image": "ci.virintox.com/spfn/rust-nex/dev-container:latest"
}

View file

@ -1,2 +1,10 @@
.env .env
target target
.dockerignore
Dockerfile
CODE_OF_CONDUCT.md
CONTRIBUTING.md
README.md
.gitignore
LICENSE
.devcontainer.json

3
.gitignore vendored
View file

@ -2,4 +2,5 @@ target
.idea .idea
.env .env
log log
reports reports
.zed

View file

@ -1,19 +1,26 @@
image: docker:latest default:
image: quay.io/podman/stable
cache:
key: image-cache
paths:
- /var/lib/containers/storage
- /run/containers/storage
- .local/share/containers/storage
before_script:
- git submodule update --init
- podman login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
variables: variables:
IMAGE_NAME: "ci.perditum.com/perditum/rnex-splatoon" DOCKER_TLS_CERTDIR: "/certs"
IMAGE_TAG: "${CI_COMMIT_REF_SLUG}" IMAGE_TAG: "${CI_COMMIT_REF_SLUG}"
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" ci.perditum.com
stages: stages:
- build-and-push - build_and_test
build-and-push: splatoon:
stage: build-and-push stage: build_and_test
script: script: ./.ci-scripts/make-edition.sh splatoon
- docker build -t "$IMAGE_NAME:$IMAGE_TAG" .
- docker tag "$IMAGE_NAME:$IMAGE_TAG" "$IMAGE_NAME:latest" friends:
- docker push "$IMAGE_NAME:$IMAGE_TAG" stage: build_and_test
- docker push "$IMAGE_NAME:latest" script: ./.ci-scripts/make-edition.sh friends

3
.gitmodules vendored
View file

@ -1,3 +0,0 @@
[submodule "grpc-protobufs"]
path = grpc-protobufs
url = https://github.com/PretendoNetwork/grpc-protobufs.git

2831
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,91 +1,8 @@
[package] [workspace]
name = "rust-nex" resolver = "3"
version = "0.1.0" members = [
edition = "2021" "macros",
"rnex-core",
[profile.prod] "prudpv1",
inherits = "release" "prudpv0"
overflow-checks = false , "proxy", "proxy-common"]
strip = true
debug = false
debug-assertions = false
lto = true
incremental = false
[dependencies]
bytemuck = { version = "1.21.0", features = ["derive"] }
dotenv = "0.15.0"
once_cell = "1.20.2"
rc4 = "0.1.0"
thiserror = "2.0.11"
v_byte_macros = { git = "https://github.com/DJMrTV/VByteMacros" }
simplelog = "0.12.2"
chrono = "0.4.39"
log = "0.4.25"
anyhow = "1.0.95"
rand = "0.8.5"
hmac = "0.12.1"
md-5 = "^0.10.6"
tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread", "net", "sync", "fs"] }
tokio-stream = { version = "0.1.17", features = ["io-util"] }
tonic = "0.12.3"
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"
futures = "0.3.31"
reqwest = "0.12.18"
json = "0.12.4"
ctrlc = "3.4.7"
rsa = "0.9.8"
sha2 = "0.10.9"
chacha20 = "0.9.1"
rustls = "0.23.27"
rustls-pki-types = "1.12.0"
rustls-webpki = "0.103.3"
tokio-rustls = "0.26.2"
tokio-tungstenite = "0.27.0"
tungstenite = "0.27.0"
[build-dependencies]
tonic-build = "0.12.3"
[features]
default = ["secure", "auth"]
secure = []
auth = []
no_tls = []
[[bin]]
name = "proxy_insecure"
path = "src/executables/proxy_insecure.rs"
[[bin]]
name = "proxy_secure"
path = "src/executables/proxy_secure.rs"
[[bin]]
name = "backend_server_insecure"
path = "src/executables/backend_server_insecure.rs"
[[bin]]
name = "backend_server_secure"
path = "src/executables/backend_server_secure.rs"
[[bin]]
name = "edge_node_holder_server"
path = "src/executables/edge_node_holder_server.rs"

View file

@ -1,20 +1,42 @@
FROM rust:alpine AS builder FROM rust:alpine AS build-container
RUN apk add --no-cache protobuf-dev git musl-dev lld openssl-dev openssl-libs-static yq bash
FROM build-container AS builder
WORKDIR /app WORKDIR /app
COPY . . COPY . .
RUN apk add --no-cache protobuf-dev git musl-dev lld openssl-dev openssl-libs-static ARG EDITION
RUN git submodule update --init --recursive RUN git submodule update --init --recursive
RUN OPENSSL_LIB_DIR=/usr/lib OPENSSL_INCLUDE_DIR=/usr/include/openssl OPENSSL_STATIC=1 RUSTFLAGS="-C relocation-model=static -C linker=ld.lld" cargo test --target x86_64-unknown-linux-musl RUN ./test-edition.sh
RUN OPENSSL_LIB_DIR=/usr/lib OPENSSL_INCLUDE_DIR=/usr/include/openssl OPENSSL_STATIC=1 RUSTFLAGS="-C relocation-model=static -C linker=ld.lld" cargo build --profile prod --target x86_64-unknown-linux-musl RUN ./build-edition.sh
FROM scratch AS final FROM scratch AS node-holder
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/edge_node_holder_server /edge_node_holder_server
ENTRYPOINT ["/edge_node_holder_server"]
# Copy the compiled binary from the builder stage
COPY --from=builder /app/target/x86_64-unknown-linux-musl/prod/splatoon-server-rust /splatoon-server-rust
# Command to run the application FROM scratch AS proxy-insecure
ENTRYPOINT ["/splatoon-server-rust"] COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/proxy_insecure /proxy_insecure
ENTRYPOINT ["/proxy_insecure"]
FROM scratch AS proxy-secure
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/proxy_secure /proxy_secure
ENTRYPOINT ["/proxy_secure"]
FROM scratch AS backend-auth
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/backend_server_insecure /backend_server_insecure
ENTRYPOINT ["/backend_server_insecure"]
FROM scratch AS backend-secure
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/backend_server_secure /backend_server_secure
ENTRYPOINT ["/backend_server_secure"]
# make sure the final output container is the dev container so that we can use it from the devcontainer.json
FROM build-container AS dev-container
RUN apk add openjdk21-jdk gcompat

View file

@ -4,4 +4,17 @@
- Pretendo team for the rest of the Servers and Reverse engineering efforts - Pretendo team for the rest of the Servers and Reverse engineering efforts
- Kinnay for his huge work on reversing nex servers and documentation(https://github.com/Kinnay/NintendoClients/) - Kinnay for his huge work on reversing nex servers and documentation(https://github.com/Kinnay/NintendoClients/)
- Splatfestival testing team for helping us test our messes of code - Splatfestival testing team for helping us test our messes of code
- The SPFN team(Andrea and DJMrTV) - The SPFN team(Bloxer, Ceantix and RusticMaple)
This nex implementation was not created and is not intended to compete with pretendo,
we wholeheartedly support pretendo.
This project would never have been possible without their reverse engineering efforts.
As such if you want to respect the Authors wishes(Maple(Me) is pretty much the sole author of Rust-Nex), do not
use it if you mean any harm to pretendo. If you do show intent to harm them you will be blocked from ever contributing
and will be refused support, as such please also refrain from asking for support if you have been blocked by pretendo.
I felt like this needed to be said as there are far too many pretendo copycats who blatantly copy their code and use
their reversal efforts with no credits in sight in an attempt to harm them for some grudge or stupid reason.
I feel that by working together and not against each other we can reach a better and healthier future for the community,
health of developers and numerous more reasons.

13
build-edition.sh Executable file
View file

@ -0,0 +1,13 @@
#!/usr/bin/env bash
if [ -z ${EDITION+x} ]; then
EDITION=$1
fi
# comma seperated list of features for the specified version
source ./buildscripts/common.sh
echo FEATURES:
echo $EDITION_FEATURES
echo ENV SETTINGS:
env
OPENSSL_LIB_DIR=/usr/lib OPENSSL_INCLUDE_DIR=/usr/include/openssl OPENSSL_STATIC=1 RUSTFLAGS="-C relocation-model=static -C linker=ld.lld" cargo build --release --features "$EDITION_FEATURES" --target x86_64-unknown-linux-musl

10
buildscripts/common.sh Executable file
View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
TMP_FEATURES_TRAILINGCOMMA=$(yq ea ".$EDITION.features" editions.yaml | sed 's/- //g' | tr '\n' ',')
export EDITION_FEATURES=${TMP_FEATURES_TRAILINGCOMMA::-1}
SETTINGS=$(yq ea ".$EDITION.settings" editions.yaml | yq 'keys[]')
IFS=$'\n'
while IFS=$'\n' read -r KEY; do
VAL=$(yq ea ".$EDITION.settings.$KEY" editions.yaml)
declare "$KEY=$VAL"
export $KEY
done <<< "$SETTINGS"

17
editions.yaml Normal file
View file

@ -0,0 +1,17 @@
splatoon:
features:
- rmc_struct_header
- prudpv1
settings:
AUTH_REPORT_VERSION: "branch:origin/project/wup-agmj build:3_8_15_2004_0"
RNEX_VIRTUAL_PORT_INSECURE: "1:10"
RNEX_VIRTUAL_PORT_SECURE: "1:10"
RNEX_DEFAULT_PORT: 6000
friends:
features:
- friends
settings:
AUTH_REPORT_VERSION: "branch:origin/feature/45925_FixAutoReconnect build:3_10_11_2006_0"
RNEX_VIRTUAL_PORT_INSECURE: "1:10"
RNEX_VIRTUAL_PORT_SECURE: "1:10"
RNEX_DEFAULT_PORT: 6000

61
flake.lock generated Normal file
View file

@ -0,0 +1,61 @@
{
"nodes": {
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1767609335,
"narHash": "sha256-feveD98mQpptwrAEggBQKJTYbvwwglSbOv53uCfH9PY=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "250481aafeb741edfe23d29195671c19b36b6dca",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1767640445,
"narHash": "sha256-UWYqmD7JFBEDBHWYcqE6s6c77pWdcU/i+bwD6XxMb8A=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "9f0c42f8bc7151b8e7e5840fb3bd454ad850d8c5",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1765674936,
"narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"root": {
"inputs": {
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

28
flake.nix Normal file
View file

@ -0,0 +1,28 @@
{
description = "rust nex server";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
};
outputs =
inputs@{
self,
nixpkgs,
flake-parts,
}:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = [
"x86_64-linux"
"aarch64-linux"
"x86_64-darwin"
"aarch64-darwin"
];
perSystem =
{ pkgs, lib, ... }:
rec {
devShells.default = import ./shell.nix { inherit pkgs; };
};
};
}

@ -1 +0,0 @@
Subproject commit 405fe9b47b416e76b21d7087b2ed11606deccfcf

113
macros/Cargo.lock generated
View file

@ -2,15 +2,55 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "bitflags"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "cfg-if"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "getrandom"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi",
]
[[package]]
name = "libc"
version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]] [[package]]
name = "macros" name = "macros"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"rand",
"syn", "syn",
] ]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.93" version = "1.0.93"
@ -29,6 +69,41 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.98" version = "2.0.98"
@ -45,3 +120,41 @@ name = "unicode-ident"
version = "1.0.16" version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags",
]
[[package]]
name = "zerocopy"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View file

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

View file

@ -48,7 +48,7 @@ impl Parse for ProtoInputParams {
fn gen_serialize_data_struct( fn gen_serialize_data_struct(
s: DataStruct, s: DataStruct,
struct_attr: Option<&Attribute>, struct_attr: Option<&Attribute>,
) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) { ) -> (proc_macro2::TokenStream, proc_macro2::TokenStream, proc_macro2::TokenStream) {
let serialize_base_content = { let serialize_base_content = {
let mut serialize_content = quote! {}; let mut serialize_content = quote! {};
@ -65,7 +65,7 @@ fn gen_serialize_data_struct(
let ident = f.ident.as_ref().unwrap(); let ident = f.ident.as_ref().unwrap();
serialize_content.append_all(quote! { serialize_content.append_all(quote! {
self.#ident.serialize(writer)?; rnex_core::rmc::structures::RmcSerialize::serialize(&self.#ident, writer)?;
}) })
} }
@ -119,6 +119,28 @@ fn gen_serialize_data_struct(
} }
}; };
let write_size = {
let mut size_content = quote! { 0 };
for f in &s.fields {
let ident = f.ident.as_ref().unwrap();
size_content.append_all(quote! {
+ rnex_core::rmc::structures::RmcSerialize::serialize_write_size(&self.#ident)?
})
}
size_content
};
let write_size = if let Some(_) = struct_attr {
quote!{ #write_size + if rnex_core::config::FEATURE_HAS_STRUCT_HEADER{ 5 } else { 0 } }
} else {
write_size
};
// generate base with extends stuff // generate base with extends stuff
let serialize_base_content = if let Some(attr) = struct_attr { let serialize_base_content = if let Some(attr) = struct_attr {
@ -143,9 +165,18 @@ fn gen_serialize_data_struct(
quote! { quote! {
#pre_inner #pre_inner
rust_nex::rmc::structures::rmc_struct::write_struct(writer, #version, |mut writer|{ rnex_core::rmc::structures::rmc_struct::write_struct(
#serialize_base_content writer,
})?; #version,
rnex_core::rmc::structures::helpers::len_of_write(
|writer|{
#serialize_base_content
}
),
|writer|{
#serialize_base_content
}
)?;
Ok(()) Ok(())
} }
@ -176,7 +207,7 @@ fn gen_serialize_data_struct(
quote! { quote! {
#pre_inner #pre_inner
Ok(rust_nex::rmc::structures::rmc_struct::read_struct(reader, #version, move |mut reader|{ Ok(rnex_core::rmc::structures::rmc_struct::read_struct(reader, #version, move |mut reader|{
#deserialize_base_content #deserialize_base_content
})?) })?)
} }
@ -184,7 +215,13 @@ fn gen_serialize_data_struct(
deserialize_base_content deserialize_base_content
}; };
(serialize_base_content, deserialize_base_content) let write_size = quote!{
fn serialize_write_size(&self) -> rnex_core::rmc::structures::Result<u32>{
Ok(#write_size)
}
};
(serialize_base_content, deserialize_base_content, write_size)
} }
#[proc_macro_derive(RmcSerialize, attributes(extends, rmc_struct))] #[proc_macro_derive(RmcSerialize, attributes(extends, rmc_struct))]
@ -210,7 +247,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
panic!("rmc struct type MUST be a struct"); panic!("rmc struct type MUST be a struct");
};*/ };*/
let (serialize_base_content, deserialize_base_content) = match derive_input.data { let (serialize_base_content, deserialize_base_content, write_size) = match derive_input.data {
Data::Struct(s) => gen_serialize_data_struct(s, struct_attr), Data::Struct(s) => gen_serialize_data_struct(s, struct_attr),
Data::Enum(e) => { Data::Enum(e) => {
let Some(repr_attr) = repr_attr else { let Some(repr_attr) = repr_attr else {
@ -221,6 +258,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
let mut inner_match_de = quote! {}; let mut inner_match_de = quote! {};
let mut inner_match_se = quote! {}; let mut inner_match_se = quote! {};
//let mut inner_match_len = quote!{};
for variant in e.variants { for variant in e.variants {
let Some((_, val)) = variant.discriminant else { let Some((_, val)) = variant.discriminant else {
@ -235,7 +273,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
let name = &field.ident; let name = &field.ident;
base.append_all(quote!{ base.append_all(quote!{
#name: <#ty as rust_nex::rmc::structures::RmcSerialize>::deserialize(reader)?, #name: <#ty as rnex_core::rmc::structures::RmcSerialize>::deserialize(reader)?,
}); });
} }
@ -248,7 +286,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
let ty = &field.ty; let ty = &field.ty;
base.append_all(quote!{ base.append_all(quote!{
<#ty as rust_nex::rmc::structures::RmcSerialize>::deserialize(reader)?, <#ty as rnex_core::rmc::structures::RmcSerialize>::deserialize(reader)?,
}); });
} }
@ -260,7 +298,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
}; };
let mut se_with_fields = quote! { let mut se_with_fields = quote! {
<#ty as rust_nex::rmc::structures::RmcSerialize>::serialize(&#val, writer)?; <#ty as rnex_core::rmc::structures::RmcSerialize>::serialize(&#val, writer)?;
}; };
match &variant.fields { match &variant.fields {
@ -270,7 +308,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
let name = &field.ident; let name = &field.ident;
se_with_fields.append_all(quote!{ se_with_fields.append_all(quote!{
<#ty as rust_nex::rmc::structures::RmcSerialize>::serialize(#name ,writer)?; <#ty as rnex_core::rmc::structures::RmcSerialize>::serialize(#name ,writer)?;
}); });
} }
} }
@ -281,7 +319,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
let ident = Ident::new(&format!("val_{}", i), Span::call_site()); let ident = Ident::new(&format!("val_{}", i), Span::call_site());
se_with_fields.append_all(quote!{ se_with_fields.append_all(quote!{
<#ty as rust_nex::rmc::structures::RmcSerialize>::serialize(#ident, writer)?; <#ty as rnex_core::rmc::structures::RmcSerialize>::serialize(#ident, writer)?;
}); });
} }
} }
@ -344,15 +382,15 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
}; };
let deserialize_base_content = quote! { let deserialize_base_content = quote! {
let val: Self = match <#ty as rust_nex::rmc::structures::RmcSerialize>::deserialize(reader)?{ let val: Self = match <#ty as rnex_core::rmc::structures::RmcSerialize>::deserialize(reader)?{
#inner_match_de #inner_match_de
v => return Err(rust_nex::rmc::structures::Error::UnexpectedValue(v as _)) v => return Err(rnex_core::rmc::structures::Error::UnexpectedValue(v as _))
}; };
Ok(val) Ok(val)
}; };
(serialize_base_content, deserialize_base_content) (serialize_base_content, deserialize_base_content, quote!{})
} }
Data::Union(_) => { Data::Union(_) => {
unimplemented!() unimplemented!()
@ -364,16 +402,17 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
let ident = derive_input.ident; let ident = derive_input.ident;
let tokens = quote! { let tokens = quote! {
impl rust_nex::rmc::structures::RmcSerialize for #ident{ impl rnex_core::rmc::structures::RmcSerialize for #ident{
fn serialize(&self, writer: &mut dyn ::std::io::Write) -> rust_nex::rmc::structures::Result<()>{ #[inline(always)]
fn serialize(&self, writer: &mut impl ::std::io::Write) -> rnex_core::rmc::structures::Result<()>{
#serialize_base_content #serialize_base_content
} }
#[inline(always)]
fn deserialize(reader: &mut dyn ::std::io::Read) -> rust_nex::rmc::structures::Result<Self>{ fn deserialize(reader: &mut impl ::std::io::Read) -> rnex_core::rmc::structures::Result<Self>{
#deserialize_base_content #deserialize_base_content
} }
#write_size
} }
}; };
@ -511,8 +550,8 @@ pub fn rmc_struct(attr: TokenStream, input: TokenStream) -> TokenStream {
} }
impl rust_nex::rmc::protocols::RmcCallable for #struct_name{ impl rnex_core::rmc::protocols::RmcCallable for #struct_name{
async fn rmc_call(&self, remote_response_connection: &rust_nex::util::SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec<u8>){ async fn rmc_call(&self, remote_response_connection: &rnex_core::util::SendingBufferConnection, 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; <Self as #ident>::rmc_call(self, remote_response_connection, protocol_id, method_id, call_id, rest).await;
} }
} }

View file

@ -39,6 +39,7 @@ impl RmcProtocolData{
// boilerplate tokens which all raw traits need // boilerplate tokens which all raw traits need
quote!{ quote!{
#[doc(hidden)] #[doc(hidden)]
#[allow(unused_must_use)]
pub trait #raw_name: #name pub trait #raw_name: #name
}.to_tokens(tokens); }.to_tokens(tokens);
@ -54,6 +55,7 @@ impl RmcProtocolData{
let raw_name = Ident::new(&format!("raw_{}", name), name.span()); let raw_name = Ident::new(&format!("raw_{}", name), name.span());
quote!{ quote!{
#[inline(always)]
async fn #raw_name async fn #raw_name
}.to_tokens(tokens); }.to_tokens(tokens);
@ -73,7 +75,7 @@ impl RmcProtocolData{
for (param_name, param_type) in parameters{ for (param_name, param_type) in parameters{
quote!{ quote!{
let Ok(#param_name) = let Ok(#param_name) =
<#param_type as rust_nex::rmc::structures::RmcSerialize>::deserialize( <#param_type as rnex_core::rmc::structures::RmcSerialize>::deserialize(
&mut cursor &mut cursor
) else ) else
}.to_tokens(tokens); }.to_tokens(tokens);
@ -84,7 +86,7 @@ impl RmcProtocolData{
quote! { quote! {
{ {
log::error!(#error_msg); log::error!(#error_msg);
return Err(rust_nex::rmc::response::ErrorCode::Core_InvalidArgument); return Err(rnex_core::rmc::response::ErrorCode::Core_InvalidArgument);
}; };
}.to_tokens(tokens) }.to_tokens(tokens)
} else { } else {
@ -116,7 +118,7 @@ impl RmcProtocolData{
quote!{ quote!{
let retval = retval?; let retval = retval?;
let mut vec = Vec::new(); let mut vec = Vec::new();
rust_nex::rmc::structures::RmcSerialize::serialize(&retval, &mut vec).ok(); rnex_core::rmc::structures::RmcSerialize::serialize(&retval, &mut vec).ok();
Ok(vec) Ok(vec)
}.to_tokens(tokens); }.to_tokens(tokens);
} }
@ -124,9 +126,10 @@ impl RmcProtocolData{
} }
quote!{ quote!{
#[inline(always)]
async fn rmc_call_proto( async fn rmc_call_proto(
&self, &self,
remote_response_connection: &rust_nex::util::SendingBufferConnection, remote_response_connection: &rnex_core::util::SendingBufferConnection,
method_id: u32, method_id: u32,
call_id: u32, call_id: u32,
data: Vec<u8>, data: Vec<u8>,
@ -165,7 +168,7 @@ impl RmcProtocolData{
}.to_tokens(tokens); }.to_tokens(tokens);
if self.has_returns { if self.has_returns {
quote! { quote! {
Err(rust_nex::rmc::response::ErrorCode::Core_NotImplemented) Err(rnex_core::rmc::response::ErrorCode::Core_NotImplemented)
}.to_tokens(tokens); }.to_tokens(tokens);
} }
}); });
@ -176,7 +179,7 @@ impl RmcProtocolData{
if *has_returns{ if *has_returns{
quote!{ quote!{
rust_nex::rmc::response::send_result( rnex_core::rmc::response::send_result(
remote_response_connection, remote_response_connection,
ret, ret,
#id, #id,
@ -209,7 +212,8 @@ impl RmcProtocolData{
// boilerplate tokens which all raw traits need // boilerplate tokens which all raw traits need
quote!{ quote!{
#[doc(hidden)] #[doc(hidden)]
pub trait #remote_name: rust_nex::rmc::protocols::HasRmcConnection #[allow(unused_must_use)]
pub trait #remote_name: rnex_core::rmc::protocols::HasRmcConnection
}.to_tokens(tokens); }.to_tokens(tokens);
// generate the body of the raw protocol trait // generate the body of the raw protocol trait
@ -247,12 +251,12 @@ impl RmcProtocolData{
for (param_name, param_type) in parameters{ for (param_name, param_type) in parameters{
quote!{ quote!{
rust_nex::result::ResultExtension::display_err_or_some( rnex_core::result::ResultExtension::display_err_or_some(
<#param_type as rust_nex::rmc::structures::RmcSerialize>::serialize( <#param_type as rnex_core::rmc::structures::RmcSerialize>::serialize(
&#param_name, &#param_name,
&mut cursor &mut cursor
) )
).ok_or(rust_nex::rmc::response::ErrorCode::Core_InvalidArgument) ).ok_or(rnex_core::rmc::response::ErrorCode::Core_InvalidArgument)
}.to_tokens(tokens); }.to_tokens(tokens);
if self.has_returns { if self.has_returns {
quote! { quote! {
@ -268,25 +272,25 @@ impl RmcProtocolData{
quote!{ quote!{
let call_id = rand::random(); let call_id = rand::random();
let message = rust_nex::rmc::message::RMCMessage{ let message = rnex_core::rmc::message::RMCMessage{
call_id, call_id,
method_id: #method_id, method_id: #method_id,
protocol_id: #proto_id, protocol_id: #proto_id,
rest_of_data: send_data rest_of_data: send_data
}; };
let rmc_conn = <Self as rust_nex::rmc::protocols::HasRmcConnection>::get_connection(self); let rmc_conn = <Self as rnex_core::rmc::protocols::HasRmcConnection>::get_connection(self);
}.to_tokens(tokens); }.to_tokens(tokens);
if *has_returns{ if *has_returns{
quote!{ quote!{
rust_nex::result::ResultExtension::display_err_or_some( rnex_core::result::ResultExtension::display_err_or_some(
rmc_conn.make_raw_call(&message).await rmc_conn.make_raw_call(&message).await
).ok_or(rust_nex::rmc::response::ErrorCode::Core_Exception) ).ok_or(rnex_core::rmc::response::ErrorCode::Core_Exception)
}.to_tokens(tokens); }.to_tokens(tokens);
} else { } else {
quote!{ quote!{
rust_nex::result::ResultExtension::display_err_or_some( rnex_core::result::ResultExtension::display_err_or_some(
rmc_conn.make_raw_call_no_response(&message).await rmc_conn.make_raw_call_no_response(&message).await
); );
}.to_tokens(tokens); }.to_tokens(tokens);
@ -325,10 +329,5 @@ impl ToTokens for RmcProtocolData{
self.generate_raw_trait(tokens); self.generate_raw_trait(tokens);
self.generate_raw_info(tokens); self.generate_raw_info(tokens);
self.generate_raw_remote_trait(tokens); self.generate_raw_remote_trait(tokens);
} }
} }

BIN
out/backend_server_secure Executable file

Binary file not shown.

BIN
perf.data Normal file

Binary file not shown.

10
proxy-common/Cargo.toml Normal file
View file

@ -0,0 +1,10 @@
[package]
name = "proxy-common"
version = "0.1.0"
edition = "2024"
[dependencies]
thiserror = "2.0.12"
rnex-core = { path = "../rnex-core", version = "0.1.1" }
tokio = { version = "1.47.0", features = ["full"] }
log = "0.4.25"

215
proxy-common/src/lib.rs Normal file
View file

@ -0,0 +1,215 @@
use log::{error, info};
use rnex_core::{
executables::common::{OWN_IP_PUBLIC, try_get_ip},
prudp::{socket_addr::PRUDPSockAddr, virtual_port::VirtualPort},
reggie::{RemoteEdgeNodeHolder, UnitPacketWrite},
rmc::{
protocols::{
OnlyRemote, RemoteDisconnectable, RmcCallable, RmcConnection, RmcPureRemoteObject,
new_rmc_gateway_connection,
},
structures::RmcSerialize,
},
rnex_proxy_common::ConnectionInitData,
util::{SendingBufferConnection, SplittableBufferConnection},
};
use std::{
env::{self, VarError},
error,
net::{AddrParseError, IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4},
ops::Deref,
panic,
str::FromStr,
sync::{Arc, LazyLock},
};
use thiserror::Error;
use tokio::net::TcpStream;
const RNEX_DEFAULT_PORT: u16 = match u16::from_str_radix(env!("RNEX_DEFAULT_PORT"), 10) {
Ok(v) => v,
Err(_) => panic!("unable to get default port from env"),
};
#[derive(Error, Debug)]
pub enum Error {
#[error("error getting environment variable \"{0}\": {1}")]
UnableToGetEnv(&'static str, VarError),
#[error("error parsing ip address environment variable \"{0}\": {1}")]
AddrParse(&'static str, AddrParseError),
#[error(
"error error getting public ip address: \n\tattempted to read from env var \"SERVER_IP_PUBLIC\" and got: {0} \n\tattempted to request from internet and failed with: {1}"
)]
PubAddrGetErr(Box<Self>, Box<dyn error::Error>),
}
impl Into<Error> for (&'static str, AddrParseError) {
fn into(self) -> Error {
Error::AddrParse(self.0, self.1)
}
}
pub struct ProxyStartupParam {
pub forward_destination: SocketAddr,
pub edge_node_holder: SocketAddr,
pub self_public: SocketAddrV4,
pub self_private: SocketAddrV4,
pub virtual_port: VirtualPort,
}
fn try_get_env<T: FromStr>(name: &'static str) -> Result<T, Error>
where
(&'static str, T::Err): Into<Error>,
{
T::from_str(&env::var(name).map_err(|e| Error::UnableToGetEnv(name, e))?)
.map_err(|e| (name, e).into())
}
pub enum ProxyType {
Insecure,
Secure,
}
const VIRTUAL_PORT_INSECURE: LazyLock<VirtualPort> =
LazyLock::new(|| VirtualPort::parse(env!("RNEX_VIRTUAL_PORT_INSECURE")).unwrap());
const VIRTUAL_PORT_SECURE: LazyLock<VirtualPort> =
LazyLock::new(|| VirtualPort::parse(env!("RNEX_VIRTUAL_PORT_SECURE")).unwrap());
impl ProxyStartupParam {
pub fn new(prox_ty: ProxyType) -> Result<Self, Error> {
let port = RNEX_DEFAULT_PORT
+ match prox_ty {
ProxyType::Insecure => 0,
ProxyType::Secure => 1,
};
let self_private = try_get_env("SERVER_IP_PRIVATE")
.unwrap_or(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, RNEX_DEFAULT_PORT));
let self_public: SocketAddrV4 = match try_get_env("SERVER_IP_PUBLIC") {
Ok(v) => v,
Err(e) => try_get_ip()
.map(|v| SocketAddrV4::new(v, RNEX_DEFAULT_PORT))
.map_err(move |v| Error::PubAddrGetErr(Box::new(e), v))?,
};
Ok(Self {
forward_destination: try_get_env("FORWARD_DESTINATION")?,
edge_node_holder: try_get_env("EDGE_NODE_HOLDER")?,
self_private,
self_public,
virtual_port: match prox_ty {
ProxyType::Insecure => *VIRTUAL_PORT_INSECURE,
ProxyType::Secure => *VIRTUAL_PORT_SECURE,
},
})
}
}
struct OnRemoteDrop<T: RemoteDisconnectable, C: FnOnce() + Send + Sync + 'static>(T, Option<C>);
impl<T: RemoteDisconnectable, C: FnOnce() + Send + Sync + 'static> Deref for OnRemoteDrop<T, C> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
// if we had something like a thread safe OnceConsume (basically the opposite of OnceLock)
// we could make C be an FnOnce
impl<T: RemoteDisconnectable + RmcPureRemoteObject, C: FnOnce() + Send + Sync + 'static>
OnRemoteDrop<T, C>
{
pub fn new(conn: RmcConnection, drop_func: C) -> Self {
Self(T::new(conn), Some(drop_func))
}
pub async fn disconnect(&self) {
self.0.disconnect().await;
}
}
impl<T: RemoteDisconnectable, C: FnOnce() + Send + Sync + 'static> RmcCallable
for OnRemoteDrop<T, C>
{
fn rmc_call(
&self,
_responder: &SendingBufferConnection,
_protocol_id: u16,
_method_id: u32,
_call_id: u32,
_rest: Vec<u8>,
) -> impl Future<Output = ()> + Send {
// maybe respond with not implemented or something
async {}
}
}
impl<T: RemoteDisconnectable, C: FnOnce() + Send + Sync + 'static> Drop for OnRemoteDrop<T, C> {
fn drop(&mut self) {
self.1.take().unwrap()();
}
}
pub async fn setup_edge_node_connection(
param: &ProxyStartupParam,
shutdown_callback: impl FnOnce() + Send + Sync + 'static,
) {
let conn = tokio::net::TcpStream::connect(&param.edge_node_holder)
.await
.unwrap();
let conn: SplittableBufferConnection = conn.into();
conn.send(
rnex_core::reggie::EdgeNodeHolderConnectOption::Register(param.self_public)
.to_data()
.unwrap(),
)
.await;
println!("{:?}", param.self_public);
//leave the inner object floating so that it gets destroyed once we disconnect
new_rmc_gateway_connection(conn, move |r| {
Arc::new(OnRemoteDrop::<RemoteEdgeNodeHolder, _>::new(
r,
shutdown_callback,
))
});
}
pub async fn new_backend_connection(
param: &ProxyStartupParam,
addr: PRUDPSockAddr,
pid: u32,
) -> Option<SplittableBufferConnection> {
info!("attempting to connect to: {}", param.forward_destination);
let mut stream = match TcpStream::connect(param.forward_destination).await {
Ok(v) => v,
Err(e) => {
error!("unable to establish connection to backend: {}", e);
return None;
}
};
if let Err(e) = stream
.send_buffer(
&ConnectionInitData {
prudpsock_addr: addr,
pid: pid,
}
.to_data()
.unwrap(),
)
.await
{
error!("unable to send establishment data to backend: {}", e);
return None;
};
Some(stream.into())
}
#[cfg(test)]
mod test {
use crate::{VIRTUAL_PORT_INSECURE, VIRTUAL_PORT_SECURE};
fn test_virtual_port_correct() {
println!("{:?}", VIRTUAL_PORT_INSECURE);
println!("{:?}", VIRTUAL_PORT_SECURE);
}
}

28
proxy/Cargo.toml Normal file
View file

@ -0,0 +1,28 @@
[package]
name = "proxy"
version = "0.1.0"
edition = "2024"
[dependencies]
tokio = { version = "1.47.0", features = ["full"] }
prudpv0 = { path = "../prudpv0", optional = true }
prudpv1 = { path = "../prudpv1", optional = true }
proxy-common = { path = "../proxy-common" }
cfg-if = "1.0.4"
rnex-core = { path = "../rnex-core", version = "0.1.1" }
log = "0.4.25"
[features]
prudpv0 = ["dep:prudpv0"]
prudpv1 = ["dep:prudpv1"]
friends = ["prudpv0", "prudpv0/friends"]
splatoon = ["prudpv1"]
[[bin]]
name = "proxy_insecure"
path = "src/insecure.rs"
[[bin]]
name = "proxy_secure"
path = "src/secure.rs"

15
proxy/src/insecure.rs Normal file
View file

@ -0,0 +1,15 @@
use proxy::edge_node_dc_callback;
use proxy_common::{ProxyStartupParam, setup_edge_node_connection};
use rnex_core::common::setup;
#[tokio::main]
async fn main() {
setup();
let param = ProxyStartupParam::new(proxy_common::ProxyType::Insecure)
.expect("unable to get startup parameters");
setup_edge_node_connection(&param, edge_node_dc_callback).await;
proxy::start_insecure(param).await;
}

19
proxy/src/lib.rs Normal file
View file

@ -0,0 +1,19 @@
use std::process::abort;
use cfg_if::cfg_if;
use log::error;
cfg_if! {
if #[cfg(feature = "prudpv0")]{
pub use prudpv0::*;
} else if #[cfg(feature = "prudpv1")] {
pub use prudpv1::*;
} else {
compile_error!("no proxy type has been set");
}
}
pub fn edge_node_dc_callback() {
error!("disconnected from node holder, aborting!");
abort()
}

14
proxy/src/secure.rs Normal file
View file

@ -0,0 +1,14 @@
use proxy::edge_node_dc_callback;
use proxy_common::{ProxyStartupParam, setup_edge_node_connection};
use rnex_core::common::setup;
#[tokio::main]
async fn main() {
setup();
let param = ProxyStartupParam::new(proxy_common::ProxyType::Secure)
.expect("unable to get startup parameters");
setup_edge_node_connection(&param, edge_node_dc_callback).await;
proxy::start_secure(param).await;
}

20
prudpv0/Cargo.toml Normal file
View file

@ -0,0 +1,20 @@
[package]
name = "prudpv0"
version = "0.1.0"
edition = "2024"
[dependencies]
rnex-core = { path = "../rnex-core", version = "0.1.1" }
tokio = { version = "1.47.0", features = ["full"] }
bytemuck = { version = "1.23.1", features = ["derive"] }
typenum = "1.18.0"
rc4 = "0.1.0"
log = "0.4.25"
cfg-if = "1.0.4"
proxy-common = {path = "../proxy-common"}
hmac = "0.12.1"
md-5 = "^0.10.6"
[features]
prudpv0 = []
friends = ["prudpv0"]

View file

@ -0,0 +1,54 @@
trait IterExtra: Iterator {
fn sum_wrapping_u8(&mut self) -> u8
where
Self::Item: Into<u8>;
fn sum_wrapping_u32(&mut self) -> u32
where
Self::Item: Into<u32>;
}
impl<T: Iterator> IterExtra for T {
fn sum_wrapping_u8(&mut self) -> u8
where
Self::Item: Into<u8>,
{
let mut sum = 0u8;
for v in self {
let val: u8 = v.into();
sum = sum.wrapping_add(val);
}
sum
}
fn sum_wrapping_u32(&mut self) -> u32
where
Self::Item: Into<u32>,
{
let mut sum = 0u32;
for v in self {
let val: u32 = v.into();
sum = sum.wrapping_add(val);
}
sum
}
}
#[inline(always)]
pub fn common_checksum(access_key: &str, data: &[u8]) -> u8 {
let leftover = data.len() % 4;
let word_sum = bytemuck::cast_slice::<_, u32>(&data[..data.len() - leftover])
.iter()
.copied()
.sum_wrapping_u32();
let checksum = access_key.as_bytes().iter().copied().sum_wrapping_u8();
let checksum = checksum.wrapping_add(
(&data[data.len() - leftover..])
.iter()
.copied()
.sum_wrapping_u8(),
);
let checksum = checksum.wrapping_add(word_sum.to_ne_bytes().into_iter().sum_wrapping_u8());
checksum
}

View file

@ -0,0 +1,5 @@
use hmac::Hmac;
use md5::Md5;
pub const ACCESS_KEY: &str = "ridfebb9";
pub type HmacMd5 = Hmac<Md5>;

View file

@ -0,0 +1,78 @@
use std::{io::Write, rc::Rc};
use hmac::Mac;
use md5::{Digest, Md5};
use rc4::{KeyInit, Rc4, StreamCipher};
use rnex_core::prudp::{
encryption::{DEFAULT_KEY, EncryptionPair},
types_flags::{TypesFlags, types::DATA},
};
use typenum::U5;
use crate::crypto::{
Crypto, CryptoInstance,
common_crypto::common_checksum,
friends_common::{ACCESS_KEY, HmacMd5},
};
pub struct InsecureInstance {
pair: EncryptionPair<Rc4<U5>>,
self_signat: [u8; 4],
remote_signat: [u8; 4],
}
impl CryptoInstance for InsecureInstance {
fn decrypt_incoming(&mut self, data: &mut [u8]) {
self.pair.recv.apply_keystream(data);
}
fn encrypt_outgoing(&mut self, data: &mut [u8]) {
self.pair.send.apply_keystream(data);
}
fn get_user_id(&self) -> u32 {
0
}
fn generate_signature(&self, types_flags: TypesFlags, data: &[u8]) -> [u8; 4] {
if types_flags.get_types() == DATA {
if data.len() == 0 {
[0x78, 0x56, 0x34, 0x12]
} else {
let mut hash = Md5::new();
hash.write(ACCESS_KEY.as_bytes()).unwrap();
let mut hmac = <HmacMd5 as Mac>::new_from_slice(&hash.finalize().as_slice())
.expect("unable to create hmac md5");
hmac.update(data);
hmac.finalize().into_bytes()[0..4].try_into().unwrap()
}
} else {
self.self_signat
}
}
}
pub struct Insecure();
impl Crypto for Insecure {
type Instance = InsecureInstance;
fn new() -> Self {
Self()
}
fn calculate_checksum(&self, data: &[u8]) -> u8 {
common_checksum(ACCESS_KEY, data)
}
fn instantiate(
&self,
packet_data: &[u8],
self_signat: [u8; 4],
remote_signat: [u8; 4],
) -> Option<(Self::Instance, Vec<u8>)> {
Some((
InsecureInstance {
pair: EncryptionPair::init_both(|| Rc4::new(&DEFAULT_KEY)),
self_signat,
remote_signat,
},
vec![],
))
}
}

View file

@ -0,0 +1,96 @@
use hmac::Mac;
use md5::{Digest, Md5};
use rc4::{KeyInit, Rc4, StreamCipher};
use rnex_core::{
executables::common::SECURE_SERVER_ACCOUNT,
nex::account::Account,
prudp::{
encryption::EncryptionPair,
ticket::read_secure_connection_data,
types_flags::{TypesFlags, types::DATA},
},
rmc::structures::RmcSerialize,
};
use std::io::Write;
use typenum::U16;
use crate::crypto::{
Crypto, CryptoInstance,
common_crypto::common_checksum,
friends_common::{ACCESS_KEY, HmacMd5},
};
pub struct SecureInstance {
pair: EncryptionPair<Rc4<U16>>,
uid: u32,
self_signat: [u8; 4],
remote_signat: [u8; 4],
}
impl CryptoInstance for SecureInstance {
fn decrypt_incoming(&mut self, data: &mut [u8]) {
self.pair.recv.apply_keystream(data);
}
fn encrypt_outgoing(&mut self, data: &mut [u8]) {
self.pair.send.apply_keystream(data);
}
fn get_user_id(&self) -> u32 {
self.uid
}
fn generate_signature(&self, types_flags: TypesFlags, data: &[u8]) -> [u8; 4] {
if types_flags.get_types() == DATA {
if data.len() == 0 {
[0x78, 0x56, 0x34, 0x12]
} else {
let mut hash = Md5::new();
hash.write(ACCESS_KEY.as_bytes()).unwrap();
let mut hmac = <HmacMd5 as Mac>::new_from_slice(&hash.finalize().as_slice())
.expect("unable to create hmac md5");
hmac.update(data);
hmac.finalize().into_bytes()[0..4].try_into().unwrap()
}
} else {
self.self_signat
}
}
}
pub struct Secure(&'static Account);
impl Crypto for Secure {
type Instance = SecureInstance;
fn new() -> Self {
Self(&SECURE_SERVER_ACCOUNT)
}
fn calculate_checksum(&self, data: &[u8]) -> u8 {
common_checksum(ACCESS_KEY, data)
}
fn instantiate(
&self,
data: &[u8],
self_signat: [u8; 4],
remote_signat: [u8; 4],
) -> Option<(Self::Instance, Vec<u8>)> {
let (session_key, pid, check_value) = read_secure_connection_data(data, &self.0)?;
let check_value_response = check_value + 1;
let data = bytemuck::bytes_of(&check_value_response);
let mut response = Vec::new();
data.serialize(&mut response).ok()?;
Some((
SecureInstance {
pair: EncryptionPair::init_both(|| {
Rc4::new_from_slice(&session_key).expect("unable to initialize rc4 stream")
}),
self_signat,
remote_signat,
uid: pid,
},
response,
))
}
}

View file

@ -0,0 +1,9 @@
use crate::crypto::Crypto;
pub struct Insecure();
impl Crypto for Insecure {
fn calculate_checksum(&self, data: &[u8]) -> u8 {
todo!()
}
}

38
prudpv0/src/crypto/mod.rs Normal file
View file

@ -0,0 +1,38 @@
use cfg_if::cfg_if;
use rnex_core::prudp::types_flags::TypesFlags;
mod common_crypto;
pub trait CryptoInstance: Send + 'static {
fn decrypt_incoming(&mut self, data: &mut [u8]);
fn encrypt_outgoing(&mut self, data: &mut [u8]);
fn generate_signature(&self, types_flags: TypesFlags, data: &[u8]) -> [u8; 4];
fn get_user_id(&self) -> u32;
}
pub trait Crypto: Send + Sync + 'static {
type Instance: CryptoInstance;
fn new() -> Self;
fn calculate_checksum(&self, data: &[u8]) -> u8;
fn instantiate(
&self,
data: &[u8],
self_signat: [u8; 4],
remote_signat: [u8; 4],
) -> Option<(Self::Instance, Vec<u8>)>;
}
cfg_if! {
if #[cfg(feature = "friends")]{
pub mod friends_common;
pub mod friends_insecure;
pub use friends_insecure::*;
pub mod friends_secure;
pub use friends_secure::*;
} else {
pub mod secure;
pub use secure::*;
pub mod insecure;
pub use insecure::*;
}
}

View file

@ -0,0 +1,9 @@
use crate::crypto::Crypto;
pub struct Secure();
impl Crypto for Secure {
fn calculate_checksum(&self, data: &[u8]) -> u8 {
todo!()
}
}

65
prudpv0/src/lib.rs Normal file
View file

@ -0,0 +1,65 @@
cfg_if::cfg_if! {
if #[cfg(feature = "prudpv0")] {
use bytemuck::{Pod, Zeroable};
use cfg_if::cfg_if;
use log::{error, info, warn};
use proxy_common::{ProxyStartupParam, setup_edge_node_connection};
use rnex_core::executables::common::{OWN_IP_PRIVATE, OWN_IP_PUBLIC, SERVER_PORT};
use rnex_core::prudp::types_flags::TypesFlags;
use rnex_core::prudp::types_flags::types::SYN;
use rnex_core::prudp::virtual_port::VirtualPort;
use rnex_core::reggie::EdgeNodeHolderConnectOption::Register;
use rnex_core::reggie::RemoteEdgeNodeHolder;
use rnex_core::rmc::protocols::{OnlyRemote, new_rmc_gateway_connection};
use rnex_core::rmc::structures::RmcSerialize;
use rnex_core::util::SplittableBufferConnection;
use std::env;
use std::net::SocketAddrV4;
use std::process::abort;
use std::sync::{Arc, LazyLock};
use tokio::net::UdpSocket;
use crate::crypto::{Crypto, Insecure, Secure};
use crate::packet::PRUDPV0Packet;
use crate::server::Server;
mod crypto;
mod packet;
mod server;
pub static EDGE_NODE_HOLDER: LazyLock<SocketAddrV4> = LazyLock::new(|| {
env::var("EDGE_NODE_HOLDER")
.ok()
.and_then(|s| s.parse().ok())
.expect("EDGE_NODE_HOLDER not set")
});
pub static FORWARD_DESTINATION: LazyLock<SocketAddrV4> = LazyLock::new(|| {
env::var("FORWARD_DESTINATION")
.ok()
.and_then(|s| s.parse().ok())
.expect("FORWARD_DESTINATION not set")
});
//same as with prudpv1 this is responsible for handeling the different cryptography
//implementations, e.g. secure and insecure(this also includes special cases like friends)
async fn start_proxy<T: Crypto>(param: ProxyStartupParam) {
info!("creating cryptography instance");
let mut crypto = Arc::new(T::new());
info!("binding to socket");
let server: Arc<Server<T>> = Arc::new(Server::new(param).await);
info!("waiting on packets");
server.run_task().await;
}
pub async fn start_secure(param: ProxyStartupParam) {
start_proxy::<Secure>(param).await;
}
pub async fn start_insecure(param: ProxyStartupParam) {
start_proxy::<Insecure>(param).await;
}
}
}

405
prudpv0/src/packet.rs Normal file
View file

@ -0,0 +1,405 @@
use std::mem::transmute;
use bytemuck::{Pod, Zeroable, try_from_bytes, try_from_bytes_mut};
use log::{error, info, warn};
use rnex_core::prudp::{
types_flags::{
self, TypesFlags,
flags::{HAS_SIZE, NEED_ACK},
types::{CONNECT, DATA, DISCONNECT, PING, SYN},
},
virtual_port::VirtualPort,
};
use crate::crypto::{Crypto, CryptoInstance};
#[repr(C, packed)]
#[derive(Clone, Copy, Pod, Zeroable, Debug)]
pub struct PRUDPV0Header {
pub source: VirtualPort,
pub destination: VirtualPort,
pub type_flags: TypesFlags,
pub session_id: u8,
pub packet_signature: [u8; 4],
pub sequence_id: u16,
}
#[repr(transparent)]
pub struct PRUDPV0Packet<T: AsRef<[u8]>>(pub T);
impl<T: AsRef<[u8]>> PRUDPV0Packet<T> {
#[inline(always)]
pub fn get_packet_specific_size(&self) -> Option<usize> {
Some(get_types_flags_size_from_types_flags(
self.header()?.type_flags,
))
}
#[inline(always)]
pub fn header(&self) -> Option<&PRUDPV0Header> {
try_from_bytes(self.0.as_ref().get(..size_of::<PRUDPV0Header>())?).ok()
}
#[inline(always)]
pub fn header_mut(&mut self) -> Option<&mut PRUDPV0Header>
where
T: AsMut<[u8]>,
{
try_from_bytes_mut(self.0.as_mut().get_mut(..size_of::<PRUDPV0Header>())?).ok()
}
#[inline(always)]
pub fn connection_signature(&self) -> Option<&[u8; 4]> {
let offset = size_of::<PRUDPV0Header>();
Some(self.0.as_ref().get(offset..offset + 4)?.try_into().ok()?)
}
#[inline(always)]
pub fn connection_signature_mut(&mut self) -> Option<&mut [u8; 4]>
where
T: AsMut<[u8]>,
{
let offset = size_of::<PRUDPV0Header>();
Some(
self.0
.as_mut()
.get_mut(offset..offset + 4)?
.try_into()
.ok()?,
)
}
#[inline(always)]
pub fn size_mut(&mut self) -> Option<&mut [u8]>
where
T: AsMut<[u8]>,
{
if self.header()?.type_flags.get_flags() & HAS_SIZE == 0 {
return None;
}
let offset = size_of::<PRUDPV0Header>() + get_type_specific_size(self.header()?.type_flags);
Some(self.0.as_mut().get_mut(offset..offset + 2)?)
}
#[inline(always)]
pub fn fragment_id_mut(&mut self) -> Option<&mut u8>
where
T: AsMut<[u8]>,
{
if self.header()?.type_flags.get_types() != DATA {
return None;
}
let offset = size_of::<PRUDPV0Header>();
Some(self.0.as_mut().get_mut(offset)?)
}
#[inline(always)]
pub fn fragment_id(&self) -> Option<&u8> {
if self.header()?.type_flags.get_types() != DATA {
return None;
}
let offset = size_of::<PRUDPV0Header>();
Some(self.0.as_ref().get(offset)?)
}
#[inline(always)]
fn get_payload_offset(&self) -> Option<usize> {
Some(size_of::<PRUDPV0Header>() + self.get_packet_specific_size()?)
}
#[inline(always)]
pub fn payload(&self) -> Option<&[u8]> {
self.0
.as_ref()
.get(self.get_payload_offset()?..(self.0.as_ref().len().saturating_sub(1)))
}
#[inline(always)]
pub fn payload_mut(&mut self) -> Option<&mut [u8]>
where
T: AsMut<[u8]>,
{
let start_offset = self.get_payload_offset()?;
let end_offset = self.0.as_ref().len().saturating_sub(1);
self.0.as_mut().get_mut(start_offset..end_offset)
}
#[inline(always)]
pub fn checksummed_data(&self) -> Option<&[u8]> {
self.0
.as_ref()
.get(..self.0.as_ref().len().saturating_sub(1))
}
#[inline(always)]
pub fn checksum(&self) -> Option<u8> {
self.0.as_ref().last().copied()
}
#[inline(always)]
pub fn checksum_mut(&mut self) -> Option<&mut u8>
where
T: AsMut<[u8]>,
{
self.0.as_mut().last_mut()
}
#[inline(always)]
pub fn check_checksum(&self, crypto: &impl Crypto) -> bool {
let Some(data) = self.checksummed_data() else {
return false;
};
let Some(checksum) = self.checksum() else {
return false;
};
if checksum != crypto.calculate_checksum(data) {
warn!(
"checksum doesnt match expected checksum: {} != {}",
checksum,
crypto.calculate_checksum(data)
)
}
checksum == crypto.calculate_checksum(data)
}
pub fn new(data: T) -> Self {
Self(data)
}
}
const DEFAULT_SIGNAT: [u8; 4] = [0x12, 0x34, 0x56, 0x78];
#[inline(always)]
const fn get_size_offset(tf: TypesFlags) -> usize {
size_of::<PRUDPV0Header>()
+ (if tf.get_types() & (SYN | CONNECT) != 0 {
4
} else if tf.get_types() & DATA != 0 {
1
} else {
0
})
}
#[inline(always)]
const fn get_type_specific_size(tf: TypesFlags) -> usize {
if tf.get_types() == SYN || tf.get_types() == CONNECT {
4
} else if tf.get_types() & DATA != 0 {
1
} else {
0
}
}
#[inline(always)]
const fn get_types_flags_size_from_types_flags(tf: TypesFlags) -> usize {
get_type_specific_size(tf) + (if tf.get_flags() & HAS_SIZE != 0 { 2 } else { 0 })
}
#[inline(always)]
pub const fn precalc_size(tf: TypesFlags, payload_size: usize) -> usize {
size_of::<PRUDPV0Header>() + get_types_flags_size_from_types_flags(tf) + payload_size + 1
}
pub fn new_syn_packet(
flags: u16,
source: VirtualPort,
destination: VirtualPort,
signat: [u8; 4],
crypto: &impl Crypto,
) -> Vec<u8> {
let type_flags = TypesFlags::default().types(SYN).flags(flags);
let vec = vec![0; precalc_size(type_flags, 0)];
let mut packet = PRUDPV0Packet::new(vec);
let header = packet.header_mut().expect("packet malformed in creation");
*header = PRUDPV0Header {
destination,
source,
packet_signature: DEFAULT_SIGNAT,
sequence_id: 0,
session_id: 0,
type_flags,
};
*packet
.connection_signature_mut()
.expect("packet malformed in creation") = signat;
*packet.checksum_mut().expect("packet malformed in creation") = crypto.calculate_checksum(
packet
.checksummed_data()
.expect("packet malformed in creation"),
);
packet.0
}
pub fn new_connect_packet(
flags: u16,
source: VirtualPort,
destination: VirtualPort,
self_signat: [u8; 4],
remote_signat: [u8; 4],
session_id: u8,
data: &[u8],
crypto: &impl Crypto,
) -> Vec<u8> {
let type_flags = TypesFlags::default().types(CONNECT).flags(flags);
let vec = vec![0; precalc_size(type_flags, data.len())];
let mut packet = PRUDPV0Packet::new(vec);
let header = packet.header_mut().expect("packet malformed in creation");
*header = PRUDPV0Header {
destination,
source,
packet_signature: self_signat,
sequence_id: 1,
session_id,
type_flags,
};
*packet
.connection_signature_mut()
.expect("packet malformed in creation") = remote_signat;
packet
.payload_mut()
.expect("packet malformed in creation")
.copy_from_slice(data);
if let Some(size) = packet.size_mut() {
size.copy_from_slice(&(data.len() as u16).to_le_bytes());
}
*packet.checksum_mut().expect("packet malformed in creation") = crypto.calculate_checksum(
packet
.checksummed_data()
.expect("packet malformed in creation"),
);
packet.0
}
pub fn new_data_packet(
flags: u16,
source: VirtualPort,
destination: VirtualPort,
data: &[u8],
sequence_id: u16,
session_id: u8,
frag_id: u8,
crypto_instance: &mut impl CryptoInstance,
crypto: &impl Crypto,
) -> Vec<u8> {
let type_flags = TypesFlags::default().types(DATA).flags(flags);
let vec = vec![0; precalc_size(type_flags, data.len())];
let mut packet = PRUDPV0Packet::new(vec);
packet
.header_mut()
.expect("packet malformed in creation")
.type_flags = type_flags;
packet
.payload_mut()
.expect("packet malformed in creation")
.copy_from_slice(data);
crypto_instance.encrypt_outgoing(packet.payload_mut().expect("packet malformed in creation"));
if let Some(size) = packet.size_mut() {
size.copy_from_slice(&(data.len() as u16).to_le_bytes());
}
*packet
.fragment_id_mut()
.expect("packet malformed in creation") = frag_id;
let packet_signature = crypto_instance.generate_signature(
type_flags,
packet.payload().expect("packet malformed in creation"),
);
let header = packet.header_mut().expect("packet malformed in creation");
*header = PRUDPV0Header {
destination,
source,
packet_signature,
sequence_id,
session_id,
type_flags,
};
*packet.checksum_mut().expect("packet malformed in creation") = crypto.calculate_checksum(
packet
.checksummed_data()
.expect("packet malformed in creation"),
);
info!("header: {:?}", packet.header());
packet.0
}
pub fn new_ping_packet(
flags: u16,
source: VirtualPort,
destination: VirtualPort,
sequence_id: u16,
session_id: u8,
crypto_instance: &mut impl CryptoInstance,
crypto: &impl Crypto,
) -> Vec<u8> {
let type_flags = TypesFlags::default().types(PING).flags(flags);
let vec = vec![0; precalc_size(type_flags, 0)];
let mut packet = PRUDPV0Packet::new(vec);
let packet_signature = crypto_instance.generate_signature(type_flags, &[]);
let header = packet.header_mut().expect("packet malformed in creation");
*header = PRUDPV0Header {
destination,
source,
packet_signature,
sequence_id,
session_id,
type_flags,
};
*packet.checksum_mut().expect("packet malformed in creation") = crypto.calculate_checksum(
packet
.checksummed_data()
.expect("packet malformed in creation"),
);
packet.0
}
pub fn new_disconnect_packet(
flags: u16,
source: VirtualPort,
destination: VirtualPort,
sequence_id: u16,
session_id: u8,
crypto_instance: &mut impl CryptoInstance,
crypto: &impl Crypto,
) -> Vec<u8> {
let type_flags = TypesFlags::default().types(DISCONNECT).flags(flags);
let vec = vec![0; precalc_size(type_flags, 0)];
let mut packet = PRUDPV0Packet::new(vec);
let packet_signature = crypto_instance.generate_signature(type_flags, &[]);
let header = packet.header_mut().expect("packet malformed in creation");
*header = PRUDPV0Header {
destination,
source,
packet_signature,
sequence_id,
session_id,
type_flags,
};
*packet.checksum_mut().expect("packet malformed in creation") = crypto.calculate_checksum(
packet
.checksummed_data()
.expect("packet malformed in creation"),
);
info!("header: {:?}", packet.header());
packet.0
}

505
prudpv0/src/server.rs Normal file
View file

@ -0,0 +1,505 @@
use std::{
collections::HashMap,
hash::Hash,
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
sync::{
Arc, LazyLock, Weak,
atomic::{AtomicBool, AtomicU32},
},
thread::sleep,
time::Duration,
};
use log::{error, info, warn};
use proxy_common::{ProxyStartupParam, new_backend_connection};
use rnex_core::{
executables::common::{OWN_IP_PRIVATE, SERVER_PORT},
prudp::{
socket_addr::PRUDPSockAddr,
types_flags::{
TypesFlags,
flags::{ACK, HAS_SIZE, NEED_ACK, RELIABLE},
types::{CONNECT, DATA, DISCONNECT, PING, SYN},
},
virtual_port::VirtualPort,
},
rnex_proxy_common::ConnectionInitData,
util::{SendingBufferConnection, SplittableBufferConnection},
};
use tokio::{
net::{TcpSocket, UdpSocket},
spawn,
sync::{Mutex, RwLock},
time::Instant,
};
use crate::{
crypto::{Crypto, CryptoInstance},
packet::{
PRUDPV0Header, PRUDPV0Packet, new_connect_packet, new_data_packet, new_disconnect_packet,
new_ping_packet, new_syn_packet, precalc_size,
},
};
pub struct InternalConnection<C: CryptoInstance> {
last_action: Instant,
crypto_instance: C,
server_packet_counter: u16,
client_packet_counter: u16,
unacknowledged_packets: HashMap<u16, Arc<Vec<u8>>>,
packet_queue: HashMap<u16, (Instant, PRUDPV0Packet<Vec<u8>>)>,
}
pub struct Connection<C: CryptoInstance> {
alive: AtomicBool,
session_id: u8,
target: SendingBufferConnection,
self_signat: [u8; 4],
remote_signat: [u8; 4],
addr: PRUDPSockAddr,
inner: Mutex<InternalConnection<C>>,
}
impl<C: CryptoInstance> InternalConnection<C> {
fn next_server_count(&mut self) -> u16 {
let prev_val = self.server_packet_counter;
let (val, _) = self.server_packet_counter.overflowing_add(1);
self.server_packet_counter = val;
prev_val
}
}
pub struct Server<C: Crypto> {
param: ProxyStartupParam,
socket: UdpSocket,
crypto: C,
connections: RwLock<HashMap<PRUDPSockAddr, Arc<Connection<C::Instance>>>>,
}
impl<C: Crypto> Server<C> {
async fn send_data_packet(self: Arc<Self>, conn: Arc<Connection<C::Instance>>, data: &[u8]) {
/*let type_flags = TypesFlags::default().types(DATA).flags(HAS_SIZE | NEED_ACK);
let vec = vec![0; precalc_size(type_flags, data.len())];
let mut packet = PRUDPV0Packet::new(vec);
let payload = packet.payload_mut().expect("packet malformed in creation");
payload.copy_from_slice(data);
let mut inner = conn.inner.lock().await;
inner.crypto_instance.encrypt_outgoing(payload);
let packet_signat = inner.crypto_instance.generate_signature(payload);
let seq = inner.next_server_count();
*packet.header_mut().expect("packet malformed in creation") = PRUDPV0Header {
source: self.param.virtual_port,
destination: conn.addr.virtual_port,
type_flags,
session_id: conn.session_id,
packet_signature: packet_signat,
sequence_id: seq,
};
/* we leave the sequence id as is for now as it defaults to 0 */
*packet.checksum_mut().expect("packet malformed in creation") =
self.crypto.calculate_checksum(
packet
.checksummed_data()
.expect("packet malformed in creation"),
);*/
let mut inner = conn.inner.lock().await;
let seq = inner.server_packet_counter;
let packet = new_data_packet(
NEED_ACK | RELIABLE,
self.param.virtual_port,
conn.addr.virtual_port,
data,
inner.server_packet_counter,
conn.session_id,
0,
&mut inner.crypto_instance,
&self.crypto,
);
inner.server_packet_counter += 1;
let packet = Arc::new(packet);
let packet_ref = Arc::downgrade(&packet);
inner.unacknowledged_packets.insert(seq, packet);
drop(inner);
let conn = Arc::downgrade(&conn);
let this = Arc::downgrade(&self);
spawn(async move {
for n in 0..5 {
let Some(data) = packet_ref.upgrade() else {
return;
};
let Some(conn) = conn.upgrade() else {
return;
};
let Some(this) = this.upgrade() else {
return;
};
info!("send attempt {}", n);
self.socket
.send_to(&data, conn.addr.regular_socket_addr)
.await;
break;
}
});
}
async fn connection_thread(
self: Arc<Self>,
conn: Weak<Connection<C::Instance>>,
mut recv: SplittableBufferConnection,
) {
while let Some(data) = recv.recv().await {
let Some(conn) = conn.upgrade() else { break };
if &data[..] == &[0, 0, 0, 0, 0] {
info!("got keepalive");
continue;
}
info!("got data from server: {:?}", data);
self.clone().send_data_packet(conn.clone(), &data).await;
}
}
async fn timeout_thread(self: Arc<Self>, conn: Arc<Connection<C::Instance>>) {
loop {
sleep(Duration::from_secs(3));
let mut inner = conn.inner.lock().await;
if (Instant::now() - inner.last_action).as_secs() > 5 {
warn!("connection exceeded silence limit, sending ping");
let packet = new_ping_packet(
NEED_ACK,
self.param.virtual_port,
conn.addr.virtual_port,
0,
conn.session_id,
&mut inner.crypto_instance,
&self.crypto,
);
self.socket
.send_to(&packet, conn.addr.regular_socket_addr)
.await;
}
if (Instant::now() - inner.last_action).as_secs() > 15 {
warn!("client timed out...");
let packet = new_disconnect_packet(
NEED_ACK,
self.param.virtual_port,
conn.addr.virtual_port,
0,
conn.session_id,
&mut inner.crypto_instance,
&self.crypto,
);
self.socket
.send_to(&packet, conn.addr.regular_socket_addr)
.await;
self.socket
.send_to(&packet, conn.addr.regular_socket_addr)
.await;
self.socket
.send_to(&packet, conn.addr.regular_socket_addr)
.await;
drop(inner);
let mut conns = self.connections.write().await;
conns.remove(&conn.addr);
drop(conns);
break;
}
drop(inner);
}
}
async fn handle_syn(self: Arc<Self>, packet: PRUDPV0Packet<Vec<u8>>, addr: PRUDPSockAddr) {
info!("got syn");
let header = packet.header().unwrap();
let signat = addr.calculate_connection_signature();
let signat = [signat[0], signat[1], signat[2], signat[3]];
let packet = new_syn_packet(ACK, header.destination, header.source, signat, &self.crypto);
self.socket.send_to(&packet, addr.regular_socket_addr).await;
}
async fn handle_connect(self: Arc<Self>, packet: PRUDPV0Packet<Vec<u8>>, addr: PRUDPSockAddr) {
let Some(data) = packet.payload() else {
warn!("malformed packet from: {:?}", addr.regular_socket_addr);
return;
};
let Some(self_signat) = packet.connection_signature().copied() else {
warn!(
"malformed packet(unable to find connection signature) from: {:?}",
addr
);
return;
};
let remote_signat = addr.calculate_connection_signature();
let remote_signat = [
remote_signat[0],
remote_signat[1],
remote_signat[2],
remote_signat[3],
];
let Some((ci, data)) = self.crypto.instantiate(&data, self_signat, remote_signat) else {
warn!("unable to instantiate crypto instance");
return;
};
let pid = ci.get_user_id();
let buf_conn = new_backend_connection(&self.param, addr, pid).await;
let Some(buf_conn) = buf_conn else {
error!("unable to connect to backend");
return;
};
let header = packet.header().expect("header should be validated by now");
let conn = Arc::new(Connection {
target: buf_conn.duplicate_sender(),
remote_signat,
self_signat,
addr,
session_id: header.session_id,
alive: AtomicBool::new(true),
inner: Mutex::new(InternalConnection {
last_action: Instant::now(),
crypto_instance: ci,
client_packet_counter: 2,
server_packet_counter: 1,
unacknowledged_packets: HashMap::new(),
packet_queue: HashMap::new(),
}),
});
let mut conns = self.connections.write().await;
conns.insert(addr, conn.clone());
drop(conns);
spawn({
let this = self.clone();
let conn = Arc::downgrade(&conn);
this.connection_thread(conn, buf_conn)
});
spawn({
let this = self.clone();
let conn = conn.clone();
this.timeout_thread(conn)
});
let packet = new_connect_packet(
ACK,
header.destination,
header.source,
self_signat,
remote_signat,
packet.header().unwrap().session_id,
&data,
&self.crypto,
);
info!("sending back connection accept");
self.socket.send_to(&packet, addr.regular_socket_addr).await;
}
async fn handle_data(self: Arc<Self>, mut packet: PRUDPV0Packet<Vec<u8>>, addr: PRUDPSockAddr) {
let Some(frag_id) = packet.fragment_id() else {
warn!("invalid packet from: {:?}", addr);
return;
};
let Some(header) = packet.header() else {
warn!("invalid packet from: {:?}", addr);
return;
};
let Some(res) = self.get_connection(addr).await else {
warn!("data packet on inactive connection from: {:?}", addr);
return;
};
info!("frag: {}", frag_id);
let mut conn = res.inner.lock().await;
let ack = new_data_packet(
ACK,
self.param.virtual_port,
res.addr.virtual_port,
&[],
header.sequence_id,
header.session_id,
*frag_id,
&mut conn.crypto_instance,
&self.crypto,
);
self.socket.send_to(&ack, addr.regular_socket_addr).await;
conn.packet_queue.insert(
packet.header().unwrap().sequence_id,
(Instant::now(), packet),
);
while let Some((_, mut packet)) = {
let ctr = conn.client_packet_counter;
conn.packet_queue.remove(&ctr)
} {
info!("processing packet: {}", conn.client_packet_counter);
let Some(payload) = packet.payload_mut() else {
//todo: at this point the stream would have been broken, we should probably disconnect the client
warn!("invalid packet from: {:?}", addr);
return;
};
conn.crypto_instance.decrypt_incoming(payload);
res.target.send(payload.to_owned()).await;
conn.client_packet_counter += 1;
}
info!("finished handeling packets, dropping inner connection");
drop(conn);
}
async fn handle_ping(self: Arc<Self>, mut packet: PRUDPV0Packet<Vec<u8>>, addr: PRUDPSockAddr) {
info!("got ping");
let header = packet.header().unwrap();
let Some(conn) = self.get_connection(addr).await else {
warn!("ping on inactive connection: {:?}", addr);
return;
};
let mut inner = conn.inner.lock().await;
let packet = new_ping_packet(
ACK,
self.param.virtual_port,
addr.virtual_port,
header.sequence_id,
header.session_id,
&mut inner.crypto_instance,
&self.crypto,
);
drop(inner);
self.socket.send_to(&packet, addr.regular_socket_addr).await;
}
async fn handle_disconnect(
self: Arc<Self>,
mut packet: PRUDPV0Packet<Vec<u8>>,
addr: PRUDPSockAddr,
) {
info!("got disconnect");
let header = packet.header().unwrap();
let Some(conn) = self.get_connection(addr).await else {
warn!("ping on inactive connection: {:?}", addr);
return;
};
let mut inner = conn.inner.lock().await;
let packet = new_disconnect_packet(
ACK,
self.param.virtual_port,
addr.virtual_port,
header.sequence_id,
header.session_id,
&mut inner.crypto_instance,
&self.crypto,
);
drop(inner);
self.socket.send_to(&packet, addr.regular_socket_addr).await;
self.socket.send_to(&packet, addr.regular_socket_addr).await;
self.socket.send_to(&packet, addr.regular_socket_addr).await;
}
async fn get_connection(&self, addr: PRUDPSockAddr) -> Option<Arc<Connection<C::Instance>>> {
let rd = self.connections.read().await;
let res = rd.get(&addr).cloned();
drop(rd);
res
}
async fn process_packet(self: Arc<Self>, packet: PRUDPV0Packet<Vec<u8>>, addr: SocketAddrV4) {
if !packet.check_checksum(&self.crypto) {
warn!("invalid checksum from: {}", addr);
return;
}
let Some(header) = packet.header() else {
warn!("malformatted packet from: {}", addr);
return;
};
info!("len: {}", packet.0.len());
let addr = PRUDPSockAddr::new(addr, header.source);
if let Some(conn) = self.get_connection(addr).await {
let mut inner = conn.inner.lock().await;
inner.last_action = Instant::now();
drop(inner);
};
if header.type_flags.get_flags() & ACK != 0 {
info!("got ack(acks are ignored for now)");
return;
}
println!("{:?}", header);
match header.type_flags.get_types() {
SYN => {
self.handle_syn(packet, addr).await;
}
CONNECT => {
self.handle_connect(packet, addr).await;
}
DATA => {
self.handle_data(packet, addr).await;
}
PING => {
self.handle_ping(packet, addr).await;
}
DISCONNECT => {
self.handle_disconnect(packet, addr).await;
}
v => {
println!("unimplemented packed type: {}", v);
}
}
}
pub async fn run_task(self: Arc<Self>) {
loop {
let mut vec: Vec<u8> = vec![0u8; 65507];
let (len, addr) = match self.socket.recv_from(&mut vec).await {
Err(e) => {
error!("unable to recv: {}", e);
break;
}
Ok(v) => v,
};
let this = self.clone();
tokio::spawn(async move {
let mut data = vec;
data.resize(len, 0);
let packet = PRUDPV0Packet::new(data);
let SocketAddr::V4(addr) = addr else {
unreachable!()
};
this.process_packet(packet, addr).await;
});
}
}
pub async fn new(param: ProxyStartupParam) -> Self {
let socket = UdpSocket::bind(param.self_private)
.await
.expect("unable to bind socket");
Self {
socket,
crypto: C::new(),
connections: RwLock::new(HashMap::new()),
param,
}
}
}

23
prudpv1/Cargo.toml Normal file
View file

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

View file

@ -0,0 +1,17 @@
use once_cell::sync::Lazy;
use std::env;
use std::net::SocketAddrV4;
pub static EDGE_NODE_HOLDER: Lazy<SocketAddrV4> = Lazy::new(|| {
env::var("EDGE_NODE_HOLDER")
.ok()
.and_then(|s| s.parse().ok())
.expect("EDGE_NODE_HOLDER not set")
});
pub static FORWARD_DESTINATION: Lazy<SocketAddrV4> = Lazy::new(|| {
env::var("FORWARD_DESTINATION")
.ok()
.and_then(|s| s.parse().ok())
.expect("FORWARD_DESTINATION not set")
});

View file

@ -0,0 +1,3 @@
pub mod common;
pub mod proxy_insecure;
pub mod proxy_secure;

View file

@ -1,53 +1,49 @@
use crate::executables::common::{EDGE_NODE_HOLDER, FORWARD_DESTINATION};
use crate::prudp::router::Router;
use crate::prudp::unsecure::Unsecure;
use log::error;
use rnex_core::common::setup;
use rnex_core::executables::common::{OWN_IP_PRIVATE, OWN_IP_PUBLIC, SERVER_PORT};
use rnex_core::prudp::virtual_port::VirtualPort;
use rnex_core::reggie::EdgeNodeHolderConnectOption::Register;
use rnex_core::reggie::RemoteEdgeNodeHolder;
use rnex_core::reggie::UnitPacketRead;
use rnex_core::reggie::UnitPacketWrite;
use rnex_core::rmc::protocols::{OnlyRemote, new_rmc_gateway_connection};
use rnex_core::rmc::structures::RmcSerialize;
use rnex_core::rnex_proxy_common::ConnectionInitData;
use rnex_core::util::SplittableBufferConnection;
use std::net::SocketAddrV4; use std::net::SocketAddrV4;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use futures::future::Remote;
use log::{error, warn};
use macros::rmc_struct;
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tokio::sync::RwLock;
use tokio::task; use tokio::task;
use tokio::time::sleep; use tokio::time::sleep;
use tokio_rustls::client::TlsStream;
use tokio_tungstenite::MaybeTlsStream;
use rust_nex::common::setup;
use rust_nex::executables::common::{AUTH_SERVER_ACCOUNT, FORWARD_DESTINATION, OWN_IP_PRIVATE, OWN_IP_PUBLIC, SECURE_EDGE_NODE_HOLDER, SECURE_SERVER_ACCOUNT, SERVER_PORT};
use rust_nex::prudp::packet::VirtualPort;
use rust_nex::prudp::router::Router;
use rust_nex::prudp::secure::Secure;
use rust_nex::prudp::unsecure::Unsecure;
use rust_nex::reggie::EdgeNodeHolderConnectOption::{DontRegister, Register};
use rust_nex::rmc::response::ErrorCode;
use rust_nex::rnex_proxy_common::ConnectionInitData;
use rust_nex::reggie::{RemoteEdgeNodeHolder, UnitPacketWrite};
use rust_nex::rmc::structures::RmcSerialize;
use rust_nex::reggie::UnitPacketRead;
use rust_nex::rmc::protocols::{new_rmc_gateway_connection, OnlyRemote, RemoteInstantiatable};
use rust_nex::util::SplittableBufferConnection;
#[tokio::main] pub async fn start() {
async fn main() { /*let conn = tokio::net::TcpStream::connect(&*EDGE_NODE_HOLDER)
setup(); .await
.unwrap();
let conn = tokio::net::TcpStream::connect(&*SECURE_EDGE_NODE_HOLDER).await.unwrap();
let conn: SplittableBufferConnection = conn.into(); let conn: SplittableBufferConnection = conn.into();
conn.send(Register(SocketAddrV4::new(*OWN_IP_PUBLIC, *SERVER_PORT)).to_data()).await; conn.send(
Register(SocketAddrV4::new(*OWN_IP_PUBLIC, *SERVER_PORT))
let conn = new_rmc_gateway_connection(conn, |r| Arc::new(OnlyRemote::<RemoteEdgeNodeHolder>::new(r))); .to_data()
.unwrap(),
)
.await;
let conn = new_rmc_gateway_connection(conn, |r| {
Arc::new(OnlyRemote::<RemoteEdgeNodeHolder>::new(r))
});*/
let (router_secure, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *SERVER_PORT)) let (router_secure, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *SERVER_PORT))
.await .await
.expect("unable to start router"); .expect("unable to start router");
let mut socket_secure = router_secure let mut socket_secure = router_secure
.add_socket(VirtualPort::new(1, 10), Secure( .add_socket(VirtualPort::new(1, 10), Unsecure("6f599f81"))
"6f599f81",
SECURE_SERVER_ACCOUNT.clone()
))
.await .await
.expect("unable to add socket"); .expect("unable to add socket");
@ -60,8 +56,7 @@ async fn main() {
}; };
task::spawn(async move { task::spawn(async move {
let mut stream let mut stream = match TcpStream::connect(*FORWARD_DESTINATION).await {
= match TcpStream::connect(*FORWARD_DESTINATION).await {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
error!("unable to connect: {}", e); error!("unable to connect: {}", e);
@ -69,26 +64,31 @@ async fn main() {
} }
}; };
if let Err(e) = stream.send_buffer(&ConnectionInitData{ if let Err(e) = stream
prudpsock_addr: conn.socket_addr, .send_buffer(
pid: conn.user_id &ConnectionInitData {
}.to_data()).await{ prudpsock_addr: conn.socket_addr,
pid: conn.user_id,
}
.to_data()
.unwrap(),
)
.await
{
error!("error connecting to backend: {}", e); error!("error connecting to backend: {}", e);
return; return;
}; };
loop { loop {
tokio::select! { tokio::select! {
data = conn.recv() => { data = conn.recv() => {
let Some(data) = data else { let Some(data) = data else {
break; return;
}; };
if let Err(e) = stream.send_buffer(&data[..]).await{ if let Err(e) = stream.send_buffer(&data[..]).await{
error!("error sending data to backend: {}", e); error!("error sending data to backend: {}", e);
break; return;
} }
}, },
data = stream.read_buffer() => { data = stream.read_buffer() => {
@ -96,10 +96,10 @@ async fn main() {
Ok(d) => d, Ok(d) => d,
Err(e) => { Err(e) => {
error!("error reveiving data from backend: {}", e); error!("error reveiving data from backend: {}", e);
break; return;
} }
}; };
if conn.send(data).await == None{ if conn.send(data).await == None{
return; return;
} }
@ -111,4 +111,4 @@ async fn main() {
} }
}); });
} }
} }

View file

@ -0,0 +1,102 @@
use crate::executables::common::{EDGE_NODE_HOLDER, FORWARD_DESTINATION};
use crate::prudp::router::Router;
use crate::prudp::secure::Secure;
use log::error;
use rnex_core::common::setup;
use rnex_core::executables::common::{
OWN_IP_PRIVATE, OWN_IP_PUBLIC, SECURE_SERVER_ACCOUNT, SERVER_PORT,
};
use rnex_core::prudp::virtual_port::VirtualPort;
use rnex_core::reggie::EdgeNodeHolderConnectOption::Register;
use rnex_core::reggie::RemoteEdgeNodeHolder;
use rnex_core::reggie::UnitPacketRead;
use rnex_core::reggie::UnitPacketWrite;
use rnex_core::rmc::protocols::{OnlyRemote, new_rmc_gateway_connection};
use rnex_core::rmc::structures::RmcSerialize;
use rnex_core::rnex_proxy_common::ConnectionInitData;
use rnex_core::util::SplittableBufferConnection;
use std::net::SocketAddrV4;
use std::sync::Arc;
use std::time::Duration;
use tokio::net::TcpStream;
use tokio::task;
use tokio::time::sleep;
pub async fn start() {
let (router_secure, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *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.clone()),
)
.await
.expect("unable to add socket");
// let conn = socket_secure.connect(auth_sockaddr).await.unwrap();
loop {
let Some(mut conn) = socket_secure.accept().await else {
error!("server crashed");
return;
};
task::spawn(async move {
let mut stream = match TcpStream::connect(*FORWARD_DESTINATION).await {
Ok(v) => v,
Err(e) => {
error!("unable to connect: {}", e);
return;
}
};
if let Err(e) = stream
.send_buffer(
&ConnectionInitData {
prudpsock_addr: conn.socket_addr,
pid: conn.user_id,
}
.to_data()
.unwrap(),
)
.await
{
error!("error connecting to backend: {}", e);
return;
};
loop {
tokio::select! {
data = conn.recv() => {
let Some(data) = data else {
return;
};
if let Err(e) = stream.send_buffer(&data[..]).await{
error!("error sending data to backend: {}", e);
return;
}
},
data = stream.read_buffer() => {
let data = match data{
Ok(d) => d,
Err(e) => {
error!("error reveiving data from backend: {}", e);
return;
}
};
if conn.send(data).await == None{
return;
}
},
_ = sleep(Duration::from_secs(10)) => {
conn.send([0,0,0,0,0].to_vec()).await;
}
}
}
});
}
}

14
prudpv1/src/lib.rs Normal file
View file

@ -0,0 +1,14 @@
cfg_if::cfg_if! {
if #[cfg(feature = "prudpv1")]{
use proxy_common::{ProxyStartupParam, setup_edge_node_connection};
pub mod executables;
pub mod prudp;
pub async fn start_secure(param: ProxyStartupParam) {
executables::proxy_secure::start().await;
}
pub async fn start_insecure(param: ProxyStartupParam) {
executables::proxy_insecure::start().await;
}
}
}

View file

@ -1,8 +1,8 @@
/*
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
pub trait AuthModule{ pub trait AuthModule{
fn get_auth_key(addr: Ipv4Addr) -> [u8; 32]; fn get_auth_key(addr: Ipv4Addr) -> [u8; 32];
} }*/
/* /*
struct AuthServerAuthModule; struct AuthServerAuthModule;

View file

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

View file

@ -3,20 +3,24 @@
// force the compiler to shut up here // force the compiler to shut up here
#![allow(unused_parens)] #![allow(unused_parens)]
use crate::prudp::packet::PacketOption::{
ConnectionSignature, FragmentId, InitialSequenceId, MaximumSubstreamId, SupportedFunctions,
};
use bytemuck::{Pod, Zeroable};
use hmac::{Hmac, Mac};
use log::{error, warn};
use md5::{Digest, Md5};
use rnex_core::prudp::socket_addr::PRUDPSockAddr;
use rnex_core::prudp::types_flags::TypesFlags;
use rnex_core::prudp::types_flags::flags::ACK;
use rnex_core::prudp::virtual_port::VirtualPort;
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use std::io; use std::io;
use std::io::{Cursor, Read, Seek, Write}; use std::io::{Cursor, Read, Seek, Write};
use std::net::SocketAddrV4; use std::net::SocketAddrV4;
use bytemuck::{Pod, Zeroable};
use hmac::{Hmac, Mac};
use log::{error, warn};
use md5::{Md5, Digest};
use thiserror::Error; use thiserror::Error;
use v_byte_macros::{SwapEndian}; use v_byte_helpers::SwapEndian;
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
use crate::prudp::packet::flags::ACK;
use crate::prudp::packet::PacketOption::{ConnectionSignature, FragmentId, InitialSequenceId, MaximumSubstreamId, SupportedFunctions};
use crate::prudp::sockaddr::PRUDPSockAddr;
type Md5Hmac = Hmac<Md5>; type Md5Hmac = Hmac<Md5>;
@ -31,117 +35,11 @@ pub enum Error {
#[error("invalid option id {0}")] #[error("invalid option id {0}")]
InvalidOptionId(u8), InvalidOptionId(u8),
#[error("option size {size} doesnt match expected option for given option id {id}")] #[error("option size {size} doesnt match expected option for given option id {id}")]
InvalidOptionSize { InvalidOptionSize { id: u8, size: u8 },
id: u8,
size: u8,
},
} }
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
#[repr(transparent)]
#[derive(Copy, Clone, Pod, Zeroable, SwapEndian, Default, Eq, PartialEq)]
pub struct TypesFlags(u16);
impl TypesFlags {
#[inline]
pub const fn get_types(self) -> u8 {
(self.0 & 0x000F) as u8
}
#[inline]
pub const fn get_flags(self) -> u16 {
(self.0 & 0xFFF0) >> 4
}
#[inline]
pub const fn types(self, val: u8) -> Self {
Self((self.0 & 0xFFF0) | (val as u16 & 0x000F))
}
#[inline]
pub const fn flags(self, val: u16) -> Self {
Self((self.0 & 0x000F) | ((val << 4) & 0xFFF0))
}
#[inline]
pub const fn set_flag(&mut self, val: u16){
self.0 |= (val & 0xFFF) << 4;
}
#[inline]
pub const fn set_types(&mut self, val: u8){
self.0 |= val as u16 & 0x0F;
}
}
pub mod flags {
pub const ACK: u16 = 0x001;
pub const RELIABLE: u16 = 0x002;
pub const NEED_ACK: u16 = 0x004;
pub const HAS_SIZE: u16 = 0x008;
pub const MULTI_ACK: u16 = 0x200;
}
pub mod types {
pub const SYN: u8 = 0x0;
pub const CONNECT: u8 = 0x1;
pub const DATA: u8 = 0x2;
pub const DISCONNECT: u8 = 0x3;
pub const PING: u8 = 0x4;
/// no idea what user is supposed to mean
pub const USER: u8 = 0x5;
}
impl Debug for TypesFlags {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let stream_type = self.get_types();
let port_number = self.get_flags();
write!(f, "TypesFlags{{ types: {}, flags: {} }}", stream_type, port_number)
}
}
#[repr(transparent)]
#[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
}
#[inline]
pub const fn get_port_number(self) -> u8 {
self.0 & 0x0F
}
#[inline]
pub fn stream_type(self, val: u8) -> Self {
let masked_val = val & 0x0F;
assert_eq!(masked_val, val);
Self((self.0 & 0x0F) | (masked_val << 4))
}
#[inline]
pub fn port_number(self, val: u8) -> Self {
let masked_val = val & 0x0F;
assert_eq!(masked_val, val);
Self((self.0 & 0xF0) | masked_val)
}
#[inline]
pub fn new(port: u8, stream_type: u8) -> Self {
Self(0).stream_type(stream_type).port_number(port)
}
}
impl Debug for VirtualPort {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let stream_type = self.get_stream_type();
let port_number = self.get_port_number();
write!(f, "VirtualPort{{ stream_type: {}, port_number: {} }}", stream_type, port_number)
}
}
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone, Pod, Zeroable, SwapEndian, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Pod, Zeroable, SwapEndian, Eq, PartialEq)]
pub struct PRUDPV1Header { pub struct PRUDPV1Header {
@ -159,7 +57,7 @@ pub struct PRUDPV1Header {
impl Default for PRUDPV1Header { impl Default for PRUDPV1Header {
fn default() -> Self { fn default() -> Self {
Self{ Self {
magic: [0xEA, 0xD0], magic: [0xEA, 0xD0],
version: 1, version: 1,
session_id: 0, session_id: 0,
@ -169,32 +67,30 @@ impl Default for PRUDPV1Header {
destination_port: VirtualPort(0), destination_port: VirtualPort(0),
types_and_flags: TypesFlags(0), types_and_flags: TypesFlags(0),
packet_specific_size: 0, packet_specific_size: 0,
substream_id: 0 substream_id: 0,
} }
} }
} }
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub enum PacketOption{ pub enum PacketOption {
SupportedFunctions(u32), SupportedFunctions(u32),
ConnectionSignature([u8; 16]), ConnectionSignature([u8; 16]),
FragmentId(u8), FragmentId(u8),
InitialSequenceId(u16), InitialSequenceId(u16),
MaximumSubstreamId(u8) MaximumSubstreamId(u8),
} }
impl PacketOption{ impl PacketOption {
fn from(option_id: OptionId, option_data: &[u8]) -> io::Result<Self>{ fn from(option_id: OptionId, option_data: &[u8]) -> io::Result<Self> {
let mut data_cursor = Cursor::new(option_data); let mut data_cursor = Cursor::new(option_data);
let val = match option_id.into(){ let val = match option_id.into() {
0 => SupportedFunctions(data_cursor.read_struct(IS_BIG_ENDIAN)?), 0 => SupportedFunctions(data_cursor.read_struct(IS_BIG_ENDIAN)?),
1 => ConnectionSignature(data_cursor.read_struct(IS_BIG_ENDIAN)?), 1 => ConnectionSignature(data_cursor.read_struct(IS_BIG_ENDIAN)?),
2 => FragmentId(data_cursor.read_struct(IS_BIG_ENDIAN)?), 2 => FragmentId(data_cursor.read_struct(IS_BIG_ENDIAN)?),
3 => InitialSequenceId(data_cursor.read_struct(IS_BIG_ENDIAN)?), 3 => InitialSequenceId(data_cursor.read_struct(IS_BIG_ENDIAN)?),
4 => MaximumSubstreamId(data_cursor.read_struct(IS_BIG_ENDIAN)?), 4 => MaximumSubstreamId(data_cursor.read_struct(IS_BIG_ENDIAN)?),
_ => unreachable!() _ => unreachable!(),
}; };
Ok(val) Ok(val)
@ -255,7 +151,7 @@ impl OptionId {
// Invariant is upheld because we only create the object if it doesn't violate the invariant // Invariant is upheld because we only create the object if it doesn't violate the invariant
match val { match val {
0 | 1 | 2 | 3 | 4 => Ok(Self(val)), 0 | 1 | 2 | 3 | 4 => Ok(Self(val)),
_ => Err(Error::InvalidOptionId(val)) _ => Err(Error::InvalidOptionId(val)),
} }
} }
@ -266,7 +162,7 @@ impl OptionId {
2 => 1, 2 => 1,
3 => 2, 3 => 2,
4 => 1, 4 => 1,
_ => unreachable!() _ => unreachable!(),
} }
} }
} }
@ -281,8 +177,7 @@ impl PRUDPV1Packet {
pub fn new(reader: &mut (impl Read + Seek)) -> Result<Self> { pub fn new(reader: &mut (impl Read + Seek)) -> Result<Self> {
let header: PRUDPV1Header = reader.read_struct(IS_BIG_ENDIAN)?; let header: PRUDPV1Header = reader.read_struct(IS_BIG_ENDIAN)?;
if header.magic[0] != 0xEA || if header.magic[0] != 0xEA || header.magic[1] != 0xD0 {
header.magic[1] != 0xD0 {
return Err(Error::InvalidMagic(u16::from_be_bytes(header.magic))); return Err(Error::InvalidMagic(u16::from_be_bytes(header.magic)));
} }
@ -290,31 +185,31 @@ impl PRUDPV1Packet {
return Err(Error::InvalidVersion(header.version)); return Err(Error::InvalidVersion(header.version));
} }
let packet_signature: [u8; 16] = reader.read_struct(IS_BIG_ENDIAN)?; let packet_signature: [u8; 16] = reader.read_struct(IS_BIG_ENDIAN)?;
//let packet_signature: [u8; 16] = [0; 16]; //let packet_signature: [u8; 16] = [0; 16];
assert_eq!(reader.stream_position().ok(), Some(14 + 16)); assert_eq!(reader.stream_position().ok(), Some(14 + 16));
let mut packet_specific_buffer = vec![0u8; header.packet_specific_size as usize]; let mut packet_specific_buffer = vec![0u8; header.packet_specific_size as usize];
reader.read_exact(&mut packet_specific_buffer)?; reader.read_exact(&mut packet_specific_buffer)?;
//no clue whats up with options but they are broken //no clue whats up with options but they are broken
let mut packet_specific_data_cursor = Cursor::new(&packet_specific_buffer); let mut packet_specific_data_cursor = Cursor::new(&packet_specific_buffer);
let mut options = Vec::new(); let mut options = Vec::new();
loop { loop {
let Ok(option_id): io::Result<u8> = packet_specific_data_cursor.read_struct(IS_BIG_ENDIAN) else { let Ok(option_id): io::Result<u8> =
break packet_specific_data_cursor.read_struct(IS_BIG_ENDIAN)
else {
break;
}; };
let Ok(value_size): io::Result<u8> = packet_specific_data_cursor.read_struct(IS_BIG_ENDIAN) else { let Ok(value_size): io::Result<u8> =
break packet_specific_data_cursor.read_struct(IS_BIG_ENDIAN)
else {
break;
}; };
if value_size == 0 { if value_size == 0 {
@ -334,20 +229,21 @@ impl PRUDPV1Packet {
} }
let mut option_data = vec![0u8; value_size as usize]; let mut option_data = vec![0u8; value_size as usize];
if packet_specific_data_cursor.read_exact(&mut option_data[..]).is_err() { if packet_specific_data_cursor
.read_exact(&mut option_data[..])
.is_err()
{
error!("unable to read options"); error!("unable to read options");
break; break;
} }
options.push(PacketOption::from(option_id, &option_data)?); options.push(PacketOption::from(option_id, &option_data)?);
} }
let mut payload = vec![0u8; header.payload_size as usize]; let mut payload = vec![0u8; header.payload_size as usize];
reader.read_exact(&mut payload)?; reader.read_exact(&mut payload)?;
Ok(Self { Ok(Self {
header, header,
packet_signature, packet_signature,
@ -356,22 +252,21 @@ impl PRUDPV1Packet {
}) })
} }
pub fn base_acknowledgement_packet(&self) -> Self{ pub fn base_acknowledgement_packet(&self) -> Self {
let base = self.base_response_packet(); let base = self.base_response_packet();
let mut flags = self.header.types_and_flags.flags(0); let mut flags = self.header.types_and_flags.flags(0);
flags.set_flag(ACK); flags.set_flag(ACK);
let options = self.options let options = self
.options
.iter() .iter()
.filter(|o| matches!(o, FragmentId(_))) .filter(|o| matches!(o, FragmentId(_)))
.cloned() .cloned()
.collect(); .collect();
Self {
Self{
header: PRUDPV1Header { header: PRUDPV1Header {
types_and_flags: flags, types_and_flags: flags,
sequence_id: self.header.sequence_id, sequence_id: self.header.sequence_id,
@ -391,17 +286,24 @@ impl PRUDPV1Packet {
} }
} }
fn generate_options_bytes(&self) -> Vec<u8>{ fn generate_options_bytes(&self) -> Vec<u8> {
let mut vec = Vec::new(); let mut vec = Vec::new();
for option in &self.options{ for option in &self.options {
option.write_to_stream(&mut vec).expect("vec should always automatically be able to extend"); option
.write_to_stream(&mut vec)
.expect("vec should always automatically be able to extend");
} }
vec vec
} }
pub fn calculate_signature_value(&self, access_key: &str, session_key: Option<[u8; 32]>, connection_signature: Option<[u8; 16]>) -> [u8; 16]{ pub fn calculate_signature_value(
&self,
access_key: &str,
session_key: Option<[u8; 32]>,
connection_signature: Option<[u8; 16]>,
) -> [u8; 16] {
let access_key_bytes = access_key.as_bytes(); let access_key_bytes = access_key.as_bytes();
let access_key_sum: u32 = access_key_bytes.iter().map(|v| *v as u32).sum(); let access_key_sum: u32 = access_key_bytes.iter().map(|v| *v as u32).sum();
let access_key_sum_bytes: [u8; 4] = access_key_sum.to_le_bytes(); let access_key_sum_bytes: [u8; 4] = access_key_sum.to_le_bytes();
@ -417,32 +319,46 @@ impl PRUDPV1Packet {
let mut hmac = Md5Hmac::new_from_slice(&key).expect("fuck"); let mut hmac = Md5Hmac::new_from_slice(&key).expect("fuck");
hmac.write(&header_data).expect("error during hmac calculation"); hmac.write(&header_data)
.expect("error during hmac calculation");
if let Some(session_key) = session_key { if let Some(session_key) = session_key {
hmac.write(&session_key).expect("error during hmac calculation"); hmac.write(&session_key)
.expect("error during hmac calculation");
} }
hmac.write(&access_key_sum_bytes).expect("error during hmac calculation"); hmac.write(&access_key_sum_bytes)
.expect("error during hmac calculation");
if let Some(connection_signature) = connection_signature { if let Some(connection_signature) = connection_signature {
hmac.write(&connection_signature).expect("error during hmac calculation"); hmac.write(&connection_signature)
.expect("error during hmac calculation");
} }
hmac.write(&option_bytes).expect("error during hmac calculation"); hmac.write(&option_bytes)
.expect("error during hmac calculation");
hmac.write_all(&self.payload).expect("error during hmac calculation"); hmac.write_all(&self.payload)
.expect("error during hmac calculation");
hmac.finalize().into_bytes()[0..16].try_into().expect("invalid hmac size") hmac.finalize().into_bytes()[0..16]
.try_into()
.expect("invalid hmac size")
} }
pub fn calculate_and_assign_signature(&mut self, access_key: &str, session_key: Option<[u8; 32]>, connection_signature: Option<[u8; 16]>){ pub fn calculate_and_assign_signature(
self.packet_signature = self.calculate_signature_value(access_key, session_key, connection_signature); &mut self,
access_key: &str,
session_key: Option<[u8; 32]>,
connection_signature: Option<[u8; 16]>,
) {
self.packet_signature =
self.calculate_signature_value(access_key, session_key, connection_signature);
} }
pub fn set_sizes(&mut self){ pub fn set_sizes(&mut self) {
self.header.packet_specific_size = self.options.iter().map(|o| o.write_size()).sum(); self.header.packet_specific_size = self.options.iter().map(|o| o.write_size()).sum();
self.header.payload_size = self.payload.len() as u16; self.header.payload_size = self.payload.len() as u16;
} }
pub fn base_response_packet(&self) -> Self { pub fn base_response_packet(&self) -> Self {
Self { Self {
header: PRUDPV1Header { header: PRUDPV1Header {
magic: [0xEA, 0xD0], magic: [0xEA, 0xD0],
@ -455,19 +371,18 @@ impl PRUDPV1Packet {
sequence_id: 0, sequence_id: 0,
session_id: 0, session_id: 0,
substream_id: 0, substream_id: 0,
}, },
packet_signature: [0; 16], packet_signature: [0; 16],
payload: Default::default(), payload: Default::default(),
options: Default::default() options: Default::default(),
} }
} }
pub fn write_to(&self, writer: &mut impl Write) -> io::Result<()>{ pub fn write_to(&self, writer: &mut impl Write) -> io::Result<()> {
writer.write_all(bytemuck::bytes_of(&self.header))?; writer.write_all(bytemuck::bytes_of(&self.header))?;
writer.write_all(&self.packet_signature)?; writer.write_all(&self.packet_signature)?;
for option in &self.options{ for option in &self.options {
option.write_to_stream(writer)?; option.write_to_stream(writer)?;
} }
@ -479,19 +394,24 @@ impl PRUDPV1Packet {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::prudp::packet::flags::{NEED_ACK, RELIABLE}; use super::{OptionId, PRUDPV1Header, PacketOption, TypesFlags};
use crate::prudp::packet::types::DATA; use rnex_core::prudp::{
use super::{OptionId, PacketOption, PRUDPV1Header, TypesFlags, VirtualPort}; types_flags::{
flags::{NEED_ACK, RELIABLE},
types::DATA,
},
virtual_port::VirtualPort,
};
#[test] #[test]
fn size_test() { fn size_test() {
assert_eq!(size_of::<PRUDPV1Header>(), 14); assert_eq!(size_of::<PRUDPV1Header>(), 14);
} }
#[test] #[test]
fn test_options(){ fn test_options() {
let packet_types = [0,1,2,3,4]; let packet_types = [0, 1, 2, 3, 4];
for p_type in packet_types{ for p_type in packet_types {
let option_id = OptionId::new(p_type).unwrap(); let option_id = OptionId::new(p_type).unwrap();
let buf = vec![0; option_id.option_type_size() as usize]; let buf = vec![0; option_id.option_type_size() as usize];
@ -505,12 +425,10 @@ mod test {
assert_eq!(write_buf.len() as u8, opt.write_size()) assert_eq!(write_buf.len() as u8, opt.write_size())
} }
} }
} }
#[test] #[test]
fn header_read(){ fn header_read() {
let header = PRUDPV1Header { let header = PRUDPV1Header {
version: 0, version: 0,
destination_port: VirtualPort(0), destination_port: VirtualPort(0),
@ -520,8 +438,8 @@ mod test {
packet_specific_size: 0, packet_specific_size: 0,
payload_size: 0, payload_size: 0,
sequence_id: 0, sequence_id: 0,
magic: [0xEA,0xD0], magic: [0xEA, 0xD0],
source_port: VirtualPort(0) source_port: VirtualPort(0),
}; };
let bytes = bytemuck::bytes_of(&header); let bytes = bytemuck::bytes_of(&header);
@ -532,11 +450,11 @@ mod test {
} }
#[test] #[test]
fn test_types_flags(){ fn test_types_flags() {
let types = TypesFlags::default().types(DATA).flags(NEED_ACK | RELIABLE); let types = TypesFlags::default().types(DATA).flags(NEED_ACK | RELIABLE);
assert_ne!((types.0 >> 4) & NEED_ACK, 0); assert_ne!((types.0 >> 4) & NEED_ACK, 0);
assert_ne!((types.0 >> 4) & RELIABLE, 0); assert_ne!((types.0 >> 4) & RELIABLE, 0);
assert_ne!((types.0 & 0xFF) as u8 & DATA, 0); assert_ne!((types.0 & 0xFF) as u8 & DATA, 0);
} }
} }

View file

@ -0,0 +1,12 @@
//! # PRUDPV1 Feature sanity checks
//!
//! Checks wether the set features are actually sensical or wether they are
//! nonsense and throws a compiler error incase of nonsense
#[cfg(feature = "friends")]
compile_error!(
"friends uses prudpv0 instead of prudpv1, please do not enable both at the same time"
);
#[cfg(feature = "prudpv0")]
compile_error!("you cannot enable two prudp versions at the same time");

View file

@ -1,33 +1,27 @@
use std::{env, io}; use std::io;
use std::io::Cursor; use std::io::Cursor;
use std::marker::PhantomData; use std::marker::PhantomData;
use tokio::net::UdpSocket; use tokio::net::UdpSocket;
use std::net::{SocketAddr, SocketAddrV4}; use std::net::{SocketAddr, SocketAddrV4};
use std::net::SocketAddr::V4; use std::net::SocketAddr::V4;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use std::sync::atomic::{AtomicBool};
use std::time::Duration; use std::time::Duration;
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
use once_cell::sync::Lazy;
use log::{error, info}; use log::{error, info};
use thiserror::Error; use thiserror::Error;
use tokio::select; use tokio::select;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use tokio::time::sleep; use tokio::time::sleep;
use crate::prudp::socket::{new_socket_pair, AnyInternalSocket, CryptoHandler, ExternalSocket}; use crate::prudp::socket::{new_socket_pair, AnyInternalSocket, CryptoHandler, ExternalSocket};
use crate::prudp::packet::{PRUDPV1Packet, VirtualPort}; use crate::prudp::packet::{PRUDPV1Packet};
use rnex_core::prudp::virtual_port::VirtualPort;
use crate::prudp::router::Error::VirtualPortTaken; use crate::prudp::router::Error::VirtualPortTaken;
static SERVER_DATAGRAMS: Lazy<u8> = Lazy::new(||{
env::var("SERVER_DATAGRAM_COUNT").ok()
.and_then(|s| s.parse().ok())
.unwrap_or(1)
});
pub struct Router { pub struct Router {
endpoints: RwLock<[Option<Arc<dyn AnyInternalSocket>>; 16]>, endpoints: RwLock<[Option<Arc<dyn AnyInternalSocket>>; 16]>,
running: AtomicBool, //running: AtomicBool,
socket: Arc<UdpSocket>, socket: Arc<UdpSocket>,
_no_outside_construction: PhantomData<()> _no_outside_construction: PhantomData<()>
} }
@ -113,7 +107,7 @@ impl Router {
let own_impl = Router { let own_impl = Router {
endpoints: Default::default(), endpoints: Default::default(),
running: AtomicBool::new(true), // running: AtomicBool::new(true),
socket: socket.clone(), socket: socket.clone(),
_no_outside_construction: Default::default() _no_outside_construction: Default::default()
}; };

View file

@ -1,110 +1,52 @@
use std::io::Cursor; use crate::prudp::packet::PRUDPV1Packet;
use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance};
use hmac::digest::consts::U32; use hmac::digest::consts::U32;
use log::error; use log::error;
use rc4::cipher::StreamCipherCoreWrapper; use rc4::cipher::StreamCipherCoreWrapper;
use rc4::{KeyInit, Rc4, Rc4Core, StreamCipher};
use rc4::consts::U16; use rc4::consts::U16;
use rc4::{KeyInit, Rc4, Rc4Core, StreamCipher};
use rnex_core::kerberos::{TicketInternalData, derive_key};
use rnex_core::nex::account::Account;
use rnex_core::prudp::encryption::EncryptionPair;
use rnex_core::prudp::ticket::read_secure_connection_data;
use rnex_core::rmc::structures::RmcSerialize;
use std::io::Cursor;
use typenum::U5; use typenum::U5;
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
use crate::kerberos::{derive_key, TicketInternalData};
use crate::nex::account::Account;
use crate::prudp::packet::PRUDPV1Packet;
use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance, EncryptionPair};
use crate::rmc::structures::RmcSerialize;
pub fn read_secure_connection_data(data: &[u8], act: &Account) -> Option<([u8; 32], u32, u32)>{
let mut cursor = Cursor::new(data);
let mut ticket_data: Vec<u8> = Vec::deserialize(&mut cursor).ok()?;
let mut request_data: Vec<u8> = Vec::deserialize(&mut cursor).ok()?;
let ticket_data_size = ticket_data.len();
let ticket_data = &mut ticket_data[0..ticket_data_size-0x10];
let server_key = derive_key(act.pid, act.kerbros_password);
let mut rc4: StreamCipherCoreWrapper<Rc4Core<U16>> =
Rc4::new_from_slice(&server_key).expect("unable to init rc4 keystream");
rc4.apply_keystream(ticket_data);
let ticket_data: &TicketInternalData = match bytemuck::try_from_bytes(ticket_data){
Ok(v) => v,
Err(e) => {
error!("unable to read internal ticket data: {}", e);
return None;
}
};
// todo: add ticket expiration
let TicketInternalData{
session_key,
pid: ticket_source_pid,
issued_time
} = *ticket_data;
// todo: add checking if tickets are signed with a valid md5-hmac
let request_data_length = request_data.len();
let request_data = &mut request_data[0.. request_data_length - 0x10];
let mut rc4: StreamCipherCoreWrapper<Rc4Core<U32>> =
Rc4::new_from_slice(&session_key).expect("unable to init rc4 keystream");
rc4.apply_keystream(request_data);
let mut reqest_data_cursor = Cursor::new(request_data);
let pid: u32 = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?;
if pid != ticket_source_pid{
let ticket_created_on = issued_time.to_regular_time();
error!("someone tried to spoof their pid, ticket was created on: {}", ticket_created_on.to_rfc2822());
return None;
}
let _cid: u32 = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?;
let response_check: u32 = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?;
Some((session_key, pid, response_check))
}
type Rc4U32 = StreamCipherCoreWrapper<Rc4Core<U32>>; type Rc4U32 = StreamCipherCoreWrapper<Rc4Core<U32>>;
pub fn generate_secure_encryption_pairs(mut session_key: [u8; 32], count: u8) -> Vec<EncryptionPair<Rc4<U32>>>{ 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); let mut vec = Vec::with_capacity(count as usize);
vec.push(EncryptionPair{ vec.push(EncryptionPair {
send: 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") recv: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"),
}); });
for _ in 1..=count{ for _ in 1..=count {
let modifier = session_key.len() + 1; let modifier = session_key.len() + 1;
let key_length = session_key.len(); let key_length = session_key.len();
for (position, val) in (&mut session_key[0..key_length/2]).iter_mut().enumerate(){ for (position, val) in (&mut session_key[0..key_length / 2]).iter_mut().enumerate() {
*val = val.wrapping_add((modifier - position) as u8); *val = val.wrapping_add((modifier - position) as u8);
} }
vec.push(EncryptionPair{ vec.push(EncryptionPair {
send: 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") recv: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"),
}); });
} }
vec vec
} }
pub struct Secure(pub &'static str, pub Account); pub struct Secure(pub &'static str, pub Account);
pub struct SecureInstance { pub struct SecureInstance {
access_key: &'static str, access_key: &'static str,
session_key: [u8; 32], session_key: [u8; 32],
@ -155,18 +97,17 @@ impl CryptoHandler for Secure {
} }
} }
impl CryptoHandlerConnectionInstance for SecureInstance { impl CryptoHandlerConnectionInstance for SecureInstance {
type Encryption = Rc4<U5>; type Encryption = Rc4<U5>;
fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]) { fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]) {
if let Some(crypt_pair) = self.streams.get_mut(substream as usize){ if let Some(crypt_pair) = self.streams.get_mut(substream as usize) {
crypt_pair.recv.apply_keystream(data); crypt_pair.recv.apply_keystream(data);
} }
} }
fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]) { fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]) {
if let Some(crypt_pair) = self.streams.get_mut(substream as usize){ if let Some(crypt_pair) = self.streams.get_mut(substream as usize) {
crypt_pair.send.apply_keystream(data); crypt_pair.send.apply_keystream(data);
} }
} }
@ -182,10 +123,14 @@ impl CryptoHandlerConnectionInstance for SecureInstance {
fn sign_packet(&self, packet: &mut PRUDPV1Packet) { fn sign_packet(&self, packet: &mut PRUDPV1Packet) {
packet.set_sizes(); packet.set_sizes();
packet.calculate_and_assign_signature(self.access_key, Some(self.session_key), Some(self.self_signature)); packet.calculate_and_assign_signature(
self.access_key,
Some(self.session_key),
Some(self.self_signature),
);
} }
fn verify_packet(&self, _packet: &PRUDPV1Packet) -> bool { fn verify_packet(&self, _packet: &PRUDPV1Packet) -> bool {
true true
} }
} }

View file

@ -1,43 +1,34 @@
use crate::prudp::packet::flags::{ACK, HAS_SIZE, MULTI_ACK, NEED_ACK, RELIABLE};
use crate::prudp::packet::types::{CONNECT, DATA, DISCONNECT, PING, SYN};
use crate::prudp::packet::PacketOption::{ use crate::prudp::packet::PacketOption::{
ConnectionSignature, FragmentId, MaximumSubstreamId, SupportedFunctions, ConnectionSignature, FragmentId, MaximumSubstreamId, SupportedFunctions,
}; };
use crate::prudp::packet::{PRUDPV1Header, PRUDPV1Packet, TypesFlags, VirtualPort}; use crate::prudp::packet::{PRUDPV1Header, PRUDPV1Packet};
use crate::prudp::sockaddr::PRUDPSockAddr;
use async_trait::async_trait; use async_trait::async_trait;
use log::info;
use log::error; use log::error;
use log::{info, warn};
use rc4::StreamCipher; use rc4::StreamCipher;
use rnex_core::prudp::socket_addr::PRUDPSockAddr;
use rnex_core::prudp::types_flags::TypesFlags;
use rnex_core::prudp::types_flags::flags::{ACK, HAS_SIZE, MULTI_ACK, NEED_ACK, RELIABLE};
use rnex_core::prudp::types_flags::types::{CONNECT, DATA, DISCONNECT, PING, SYN};
use rnex_core::prudp::virtual_port::VirtualPort;
use std::collections::{BTreeMap, HashMap}; use std::collections::{BTreeMap, HashMap};
use std::io::Cursor;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::Deref; use std::ops::Deref;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use v_byte_helpers::ReadExtensions;
use v_byte_helpers::little_endian::read_u16;
use std::time::Duration; use std::time::Duration;
use tokio::net::UdpSocket; use tokio::net::UdpSocket;
use tokio::sync::mpsc::{channel, Receiver, Sender};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tokio::time::{sleep, Instant}; use tokio::sync::mpsc::{Receiver, Sender, channel};
use tokio::time::{Instant, sleep};
// due to the way this is designed crashing the router thread causes deadlock, sorry ;-; // due to the way this is designed crashing the router thread causes deadlock, sorry ;-;
// (maybe i will fix that some day) // (maybe i will fix that some day)
/// PRUDP Socket for accepting connections to then send and recieve data from those clients /// PRUDP Socket for accepting connections to then send and recieve data from those clients
pub struct EncryptionPair<T: StreamCipher + Send> {
pub send: T,
pub recv: T,
}
impl<T: StreamCipher + Send> EncryptionPair<T> {
pub fn init_both<F: Fn() -> T>(func: F) -> Self {
Self {
recv: func(),
send: func(),
}
}
}
pub struct CommonConnection { pub struct CommonConnection {
pub user_id: u32, pub user_id: u32,
pub socket_addr: PRUDPSockAddr, pub socket_addr: PRUDPSockAddr,
@ -50,12 +41,14 @@ struct InternalConnection<E: CryptoHandlerConnectionInstance> {
connections: Weak<Mutex<BTreeMap<PRUDPSockAddr, Arc<Mutex<InternalConnection<E>>>>>>, connections: Weak<Mutex<BTreeMap<PRUDPSockAddr, Arc<Mutex<InternalConnection<E>>>>>>,
reliable_server_counter: u16, reliable_server_counter: u16,
reliable_client_counter: u16, reliable_client_counter: u16,
supported_function_version: u32,
// maybe add connection id(need to see if its even needed) // maybe add connection id(need to see if its even needed)
crypto_handler_instance: E, crypto_handler_instance: E,
data_sender: Sender<Vec<u8>>, data_sender: Sender<Vec<u8>>,
socket: Arc<UdpSocket>, socket: Arc<UdpSocket>,
packet_queue: HashMap<u16, PRUDPV1Packet>, packet_queue: HashMap<u16, PRUDPV1Packet>,
last_packet_time: Instant, last_packet_time: Instant,
unacknowleged_packets: Vec<(Instant, PRUDPV1Packet)>,
} }
impl<E: CryptoHandlerConnectionInstance> Deref for InternalConnection<E> { impl<E: CryptoHandlerConnectionInstance> Deref for InternalConnection<E> {
@ -66,6 +59,7 @@ impl<E: CryptoHandlerConnectionInstance> Deref for InternalConnection<E> {
} }
impl<E: CryptoHandlerConnectionInstance> InternalConnection<E> { impl<E: CryptoHandlerConnectionInstance> InternalConnection<E> {
/// gives back the next server packet sequence id which the client expects to send, incrementing it in the process
fn next_server_count(&mut self) -> u16 { fn next_server_count(&mut self) -> u16 {
let prev_val = self.reliable_server_counter; let prev_val = self.reliable_server_counter;
let (val, _) = self.reliable_server_counter.overflowing_add(1); let (val, _) = self.reliable_server_counter.overflowing_add(1);
@ -74,20 +68,30 @@ impl<E: CryptoHandlerConnectionInstance> InternalConnection<E> {
prev_val prev_val
} }
/// Sends a raw packet to a given client on the connection
///
/// a raw packet is one which does not get processed any further(other than to send it
/// off without buffering or anything),
/// as such you need to make sure that
/// the sizes are set correctly and so on
#[inline] #[inline]
async fn send_raw_packet(&self, mut prudp_packet: PRUDPV1Packet) { async fn send_raw_packet(&self, prudp_packet: &PRUDPV1Packet) {
prudp_packet.set_sizes(); send_raw_prudp_to_sockaddr(&self.socket, self.socket_addr, prudp_packet).await;
}
let mut vec = Vec::new(); async fn delete_connection(&self) {
let Some(conns) = self.connections.upgrade() else {
// this is fine as it implies the server has already quit, thus meaning that we dont
// have to remove ourselves from the server
return;
};
prudp_packet let mut conns = conns.lock().await;
.write_to(&mut vec)
.expect("somehow failed to convert backet to bytes");
self.socket conns.remove(&self.socket_addr);
.send_to(&vec, self.socket_addr.regular_socket_addr)
.await // the connection will now drop as soon as we leave this due to no longer having a permanent
.expect("failed to send data back"); // reference
} }
} }
@ -195,7 +199,9 @@ impl<T: CryptoHandlerConnectionInstance> AnyInternalConnection for InternalConne
self.crypto_handler_instance.sign_packet(&mut packet); self.crypto_handler_instance.sign_packet(&mut packet);
self.send_raw_packet(packet).await; self.send_raw_packet(&packet).await;
self.unacknowleged_packets.push((Instant::now(), packet));
} }
async fn close_connection(&mut self) { async fn close_connection(&mut self) {
@ -222,23 +228,29 @@ impl<T: CryptoHandlerConnectionInstance> AnyInternalConnection for InternalConne
self.crypto_handler_instance.sign_packet(&mut packet); self.crypto_handler_instance.sign_packet(&mut packet);
self.send_raw_packet(packet).await; self.send_raw_packet(&packet).await;
let Some(conns) = self.connections.upgrade() else { self.delete_connection().await;
// this is fine as it implies the server has already quit, thus meaning that we dont
// have to remove ourselves from the server
return;
};
let mut conns = conns.lock().await;
conns.remove(&self.socket_addr);
// the connection will now drop as soon as we leave this due to no longer having a permanent
// reference
} }
} }
async fn send_raw_prudp_to_sockaddr(
udp_socket: &UdpSocket,
dest: PRUDPSockAddr,
packet: &PRUDPV1Packet,
) {
let mut vec = Vec::new();
packet
.write_to(&mut vec)
.expect("somehow failed to convert backet to bytes");
udp_socket
.send_to(&vec, dest.regular_socket_addr)
.await
.expect("failed to send data back");
}
impl<T: CryptoHandler> InternalSocket<T> { impl<T: CryptoHandler> InternalSocket<T> {
async fn get_connection( async fn get_connection(
&self, &self,
@ -256,19 +268,12 @@ impl<T: CryptoHandler> InternalSocket<T> {
Some(conn) Some(conn)
} }
async fn send_packet_unbuffered(&self, dest: PRUDPSockAddr, mut packet: PRUDPV1Packet) { /// sends a raw packet to a specific prudp socket address
packet.set_sizes(); ///
/// a raw packet is a packet is a packet which wont get processed any further,
let mut vec = Vec::new(); /// sizes signatures etc need to be set before using this function
async fn send_packet_unbuffered(&self, dest: PRUDPSockAddr, packet: &PRUDPV1Packet) {
packet send_raw_prudp_to_sockaddr(&self.socket, dest, packet).await
.write_to(&mut vec)
.expect("somehow failed to convert backet to bytes");
self.socket
.send_to(&vec, dest.regular_socket_addr)
.await
.expect("failed to send data back");
} }
async fn handle_syn(&self, address: PRUDPSockAddr, packet: PRUDPV1Packet) { async fn handle_syn(&self, address: PRUDPSockAddr, packet: PRUDPV1Packet) {
@ -303,7 +308,7 @@ impl<T: CryptoHandler> InternalSocket<T> {
//println!("got syn: {:?}", response); //println!("got syn: {:?}", response);
self.send_packet_unbuffered(address, response).await; self.send_packet_unbuffered(address, &response).await;
} }
async fn connection_thread( async fn connection_thread(
@ -315,7 +320,7 @@ impl<T: CryptoHandler> InternalSocket<T> {
let mut conn = conn.lock().await; let mut conn = conn.lock().await;
if conn.last_packet_time < (Instant::now() - Duration::from_secs(5)) { if conn.last_packet_time < (Instant::now() - Duration::from_secs(5)) {
conn.send_raw_packet(PRUDPV1Packet { conn.send_raw_packet(&PRUDPV1Packet {
header: PRUDPV1Header { header: PRUDPV1Header {
sequence_id: 0, sequence_id: 0,
substream_id: 0, substream_id: 0,
@ -335,9 +340,24 @@ impl<T: CryptoHandler> InternalSocket<T> {
if conn.last_packet_time < (Instant::now() - Duration::from_secs(30)) { if conn.last_packet_time < (Instant::now() - Duration::from_secs(30)) {
conn.close_connection().await; conn.close_connection().await;
} }
for (send_time, packet) in &conn.unacknowleged_packets {
if *send_time < (Instant::now() - Duration::from_millis(3000)) {
warn!(
"failed to resend packet 5 times and never got response, destroying connection"
);
conn.close_connection().await;
break;
}
if *send_time < (Instant::now() - Duration::from_millis(500)) {
info!("unacknowledged packet sat arround for more than 500 ms, resending");
conn.send_raw_packet(packet).await;
}
}
drop(conn); drop(conn);
sleep(Duration::from_secs(5)).await; sleep(Duration::from_millis(500)).await;
} }
} }
@ -347,6 +367,7 @@ impl<T: CryptoHandler> InternalSocket<T> {
socket_addr: PRUDPSockAddr, socket_addr: PRUDPSockAddr,
session_id: u8, session_id: u8,
is_instantiator: bool, is_instantiator: bool,
supported_function_version: u32,
) { ) {
let common = Arc::new(CommonConnection { let common = Arc::new(CommonConnection {
user_id: crypto_handler_instance.get_user_id(), user_id: crypto_handler_instance.get_user_id(),
@ -367,6 +388,8 @@ impl<T: CryptoHandler> InternalSocket<T> {
socket: self.socket.clone(), socket: self.socket.clone(),
packet_queue: Default::default(), packet_queue: Default::default(),
last_packet_time: Instant::now(), last_packet_time: Instant::now(),
unacknowleged_packets: Vec::new(),
supported_function_version,
}; };
let internal = Arc::new(Mutex::new(internal)); let internal = Arc::new(Mutex::new(internal));
@ -444,12 +467,17 @@ impl<T: CryptoHandler> InternalSocket<T> {
.options .options
.push(ConnectionSignature(Default::default())); .push(ConnectionSignature(Default::default()));
let mut functions: u32 = 0;
for option in &packet.options { for option in &packet.options {
match option { match option {
MaximumSubstreamId(max_substream) => { MaximumSubstreamId(max_substream) => {
response.options.push(MaximumSubstreamId(*max_substream)) response.options.push(MaximumSubstreamId(*max_substream))
} }
SupportedFunctions(funcs) => response.options.push(SupportedFunctions(*funcs & 0xFF)), SupportedFunctions(funcs) => {
functions = *funcs & 0xFF;
response.options.push(SupportedFunctions(*funcs & 0xFF));
}
_ => { /* ? */ } _ => { /* ? */ }
} }
} }
@ -460,10 +488,10 @@ impl<T: CryptoHandler> InternalSocket<T> {
//println!("connect out: {:?}", response); //println!("connect out: {:?}", response);
self.create_connection(crypto, address, session_id, false) self.create_connection(crypto, address, session_id, false, functions)
.await; .await;
self.send_packet_unbuffered(address, response).await; self.send_packet_unbuffered(address, &response).await;
} }
async fn handle_data(&self, address: PRUDPSockAddr, packet: PRUDPV1Packet) { async fn handle_data(&self, address: PRUDPSockAddr, packet: PRUDPV1Packet) {
@ -502,7 +530,7 @@ impl<T: CryptoHandler> InternalSocket<T> {
conn.crypto_handler_instance.sign_packet(&mut response); conn.crypto_handler_instance.sign_packet(&mut response);
self.send_packet_unbuffered(address, response).await; self.send_packet_unbuffered(address, &response).await;
conn.data_sender.send(packet.payload).await.ok(); conn.data_sender.send(packet.payload).await.ok();
@ -528,7 +556,7 @@ impl<T: CryptoHandler> InternalSocket<T> {
conn.crypto_handler_instance.sign_packet(&mut response); conn.crypto_handler_instance.sign_packet(&mut response);
self.send_packet_unbuffered(address, response).await; self.send_packet_unbuffered(address, &response).await;
} }
async fn handle_disconnect(&self, address: PRUDPSockAddr, packet: PRUDPV1Packet) { async fn handle_disconnect(&self, address: PRUDPSockAddr, packet: PRUDPV1Packet) {
@ -548,10 +576,11 @@ impl<T: CryptoHandler> InternalSocket<T> {
conn.crypto_handler_instance.sign_packet(&mut response); conn.crypto_handler_instance.sign_packet(&mut response);
self.send_packet_unbuffered(address, response.clone()).await; self.send_packet_unbuffered(address, &response).await;
self.send_packet_unbuffered(address, response.clone()).await; self.send_packet_unbuffered(address, &response).await;
self.send_packet_unbuffered(address, response).await; self.send_packet_unbuffered(address, &response).await;
conn.delete_connection().await;
//self.internal_connections.lock().await; //self.internal_connections.lock().await;
} }
} }
@ -571,7 +600,6 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
if (packet.header.types_and_flags.get_flags() & ACK) != 0 { if (packet.header.types_and_flags.get_flags() & ACK) != 0 {
info!("got ack"); info!("got ack");
if packet.header.types_and_flags.get_types() == SYN if packet.header.types_and_flags.get_types() == SYN
|| packet.header.types_and_flags.get_types() == CONNECT || packet.header.types_and_flags.get_types() == CONNECT
{ {
@ -596,13 +624,76 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
} else { } else {
error!("got connection response without the active reciever being present"); error!("got connection response without the active reciever being present");
} }
return;
}
info!("got ack");
if let Some(conn) = self.get_connection(address).await {
let mut conn = conn.lock().await;
// remove the packet whose sequence id matches the ack packet
// or in other words keep all of those which dont match the sequence id
conn.unacknowleged_packets
.retain_mut(|v| packet.header.sequence_id != v.1.header.sequence_id);
} else {
error!("non connection acknowledgement packet on nonexistent connection...")
} }
return; return;
} }
if (packet.header.types_and_flags.get_flags() & MULTI_ACK) != 0 { if (packet.header.types_and_flags.get_flags() & MULTI_ACK) != 0 {
info!("got multi ack"); if let Some(conn) = self.get_connection(address).await {
let mut conn = conn.lock().await;
if conn.supported_function_version == 1 {
let mut collected_ids: Vec<u16> = Vec::new();
let mut cursor = Cursor::new(&packet.payload);
while let Ok(v) = read_u16(&mut cursor) {
collected_ids.push(v);
}
conn.unacknowleged_packets.retain_mut(|(_, up)| {
!(collected_ids.iter().any(|id| up.header.sequence_id == *id)
|| up.header.sequence_id <= packet.header.sequence_id)
});
} else {
let mut collected_ids: Vec<u16> = Vec::new();
let mut cursor = Cursor::new(&packet.payload);
let Ok(_substream_id): Result<u8, _> = cursor.read_le_struct() else {
error!("invalid data whilest reading new version agregate acknowledgement");
return;
};
let Ok(additional_sequence_ids): Result<u8, _> = cursor.read_le_struct() else {
error!("invalid data whilest reading new version agregate acknowledgement");
return;
};
let Ok(sequence_id): Result<u16, _> = cursor.read_le_struct() else {
error!("invalid data whilest reading new version agregate acknowledgement");
return;
};
for _ in 0..additional_sequence_ids {
let Ok(additional_sequence_id): Result<u16, _> = cursor.read_le_struct()
else {
error!(
"invalid data whilest reading new version agregate acknowledgement"
);
return;
};
collected_ids.push(additional_sequence_id);
}
conn.unacknowleged_packets.retain_mut(|(_, up)| {
!(collected_ids.iter().any(|id| up.header.sequence_id == *id)
|| up.header.sequence_id <= sequence_id)
});
}
} else {
error!("non connection acknowledgement packet on nonexistent connection...")
}
return; return;
} }
@ -630,7 +721,7 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
let remote_signature = address.calculate_connection_signature(); let remote_signature = address.calculate_connection_signature();
let packet = PRUDPV1Packet { let mut packet = PRUDPV1Packet {
header: PRUDPV1Header { header: PRUDPV1Header {
source_port: self.virtual_port, source_port: self.virtual_port,
destination_port: address.virtual_port, destination_port: address.virtual_port,
@ -645,7 +736,9 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
..Default::default() ..Default::default()
}; };
self.send_packet_unbuffered(address, packet).await; packet.set_sizes();
self.send_packet_unbuffered(address, &packet).await;
let Some(syn_ack_packet) = recv.recv().await else { let Some(syn_ack_packet) = recv.recv().await else {
error!("what"); error!("what");
@ -661,7 +754,7 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
return None; return None;
}; };
let packet = PRUDPV1Packet { let mut packet = PRUDPV1Packet {
header: PRUDPV1Header { header: PRUDPV1Header {
source_port: self.virtual_port, source_port: self.virtual_port,
destination_port: address.virtual_port, destination_port: address.virtual_port,
@ -676,9 +769,11 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
..Default::default() ..Default::default()
}; };
self.send_packet_unbuffered(address, packet).await; packet.set_sizes();
let Some(connect_ack_packet) = recv.recv().await else { self.send_packet_unbuffered(address, &packet).await;
let Some(_connect_ack_packet) = recv.recv().await else {
error!("what"); error!("what");
return None; return None;
}; };
@ -688,7 +783,7 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
.instantiate(remote_signature, *own_signature, &[], 1)?; .instantiate(remote_signature, *own_signature, &[], 1)?;
//todo: make this work for secure servers as well //todo: make this work for secure servers as well
self.create_connection(crypt, address, 0, true).await; self.create_connection(crypt, address, 0, true, 4).await;
Some(()) Some(())
} }

View file

@ -1,13 +1,12 @@
use crate::prudp::packet::PRUDPV1Packet;
use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use rc4::{Key, KeyInit, Rc4, StreamCipher}; use rc4::{Key, KeyInit, Rc4, StreamCipher};
use rnex_core::prudp::encryption::{DEFAULT_KEY, EncryptionPair};
use typenum::U5; use typenum::U5;
use crate::prudp::packet::PRUDPV1Packet;
use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance, EncryptionPair};
pub struct Unsecure(pub &'static str); pub struct Unsecure(pub &'static str);
pub struct UnsecureInstance { pub struct UnsecureInstance {
key: &'static str, key: &'static str,
streams: Vec<EncryptionPair<Rc4<U5>>>, streams: Vec<EncryptionPair<Rc4<U5>>>,
@ -18,7 +17,6 @@ pub struct UnsecureInstance {
// my hand was forced to use lazy so that we can guarantee this code // 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) // 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 // 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 { impl CryptoHandler for Unsecure {
type CryptoConnectionInstance = UnsecureInstance; type CryptoConnectionInstance = UnsecureInstance;
@ -53,13 +51,13 @@ impl CryptoHandlerConnectionInstance for UnsecureInstance {
type Encryption = Rc4<U5>; type Encryption = Rc4<U5>;
fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]) { fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]) {
if let Some(crypt_pair) = self.streams.get_mut(substream as usize){ if let Some(crypt_pair) = self.streams.get_mut(substream as usize) {
crypt_pair.recv.apply_keystream(data); crypt_pair.recv.apply_keystream(data);
} }
} }
fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]) { fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]) {
if let Some(crypt_pair) = self.streams.get_mut(substream as usize){ if let Some(crypt_pair) = self.streams.get_mut(substream as usize) {
crypt_pair.send.apply_keystream(data); crypt_pair.send.apply_keystream(data);
} }
} }
@ -78,7 +76,7 @@ impl CryptoHandlerConnectionInstance for UnsecureInstance {
packet.calculate_and_assign_signature(self.key, None, Some(self.self_signature)); packet.calculate_and_assign_signature(self.key, None, Some(self.self_signature));
} }
fn verify_packet(&self, packet: &PRUDPV1Packet) -> bool { fn verify_packet(&self, _packet: &PRUDPV1Packet) -> bool {
true true
} }
} }

53
rnex-core/Cargo.toml Normal file
View file

@ -0,0 +1,53 @@
[package]
name = "rnex-core"
version = "0.1.1"
edition = "2024"
[dependencies]
bytemuck = { version = "1.21.0", features = ["derive"] }
dotenv = "0.15.0"
once_cell = "1.20.2"
rc4 = "0.1.0"
thiserror = "2.0.11"
v-byte-helpers = { git = "https://github.com/RusticMaple/VByteMacros", version = "0.1.1" }
simplelog = "0.12.2"
chrono = "0.4.39"
log = "0.4.25"
rand = "0.8.5"
cfg-if = "1.0.4"
hmac = "0.12.1"
md-5 = "^0.10.6"
tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread", "net", "sync", "fs"] }
hex = "0.4.3"
macros = { path = "../macros" }
paste = "1.0.15"
typenum = "1.18.0"
json = "0.12.4"
anyhow = "1.0.100"
ureq = "3.1.4"
[dev-dependencies]
# criterion = "0.7.0"
[features]
rmc_struct_header = []
guest_login = []
friends = ["guest_login"]
[[bench]]
name = "rmc_serialization"
harness = false
[[bin]]
name = "backend_server_insecure"
path = "src/executables/backend_server_insecure.rs"
[[bin]]
name = "backend_server_secure"
path = "src/executables/backend_server_secure.rs"
[[bin]]
name = "edge_node_holder_server"
path = "src/executables/edge_node_holder_server.rs"

View file

@ -0,0 +1,99 @@
use std::hint::black_box;
use std::io::Cursor;
use std::ops::Deref;
use criterion::{criterion_group, criterion_main, Criterion};
use once_cell::sync::Lazy;
use rnex_core::kerberos::KerberosDateTime;
use rnex_core::rmc::structures::matchmake::{AutoMatchmakeParam, Gathering, MatchmakeParam, MatchmakeSession, MatchmakeSessionSearchCriteria};
use rnex_core::rmc::structures::RmcSerialize;
use rnex_core::rmc::structures::variant::Variant;
static DUMMY: Lazy<AutoMatchmakeParam> = Lazy::new(|| AutoMatchmakeParam{
additional_participants: vec![1,2,3,4],
auto_matchmake_option: 10,
gid_for_participation_check: 9,
join_message: "hi".to_string(),
participation_count: 32,
target_gids: vec![45,2,51,1,1,1,1],
search_criteria: vec![MatchmakeSessionSearchCriteria{
attribs: vec!["hi".to_string(), "ig".to_string(), "gotta put data here".to_string()],
exclude_locked: true,
exclude_non_host_pid: false,
exclude_system_password_set: true,
exclude_user_password_set: false,
game_mode: "some gamemode".to_string(),
matchmake_param: MatchmakeParam{
params: vec![
("SR".to_string(), Variant::Bool(true)),
("SR2".to_string(), Variant::Double(1.0)),
("SR3".to_string(), Variant::SInt64(42)),
("SR4".to_string(), Variant::String("test".to_string()))
]
},
matchmake_system_type: "some type".to_string(),
maximum_participants: "???".to_string(),
minimum_participants: "-99".to_string(),
refer_gid: 123,
selection_method: 9999999,
vacant_only: true,
vacant_participants: 1000
}],
matchmake_session: MatchmakeSession{
refer_gid: 10,
matchmake_system_type: 139,
matchmake_param: MatchmakeParam{
params: vec![
("QSR".to_string(), Variant::Bool(false)),
("SRQ2".to_string(), Variant::Double(1.1)),
("SQR3".to_string(), Variant::SInt64(422)),
("SDR4".to_string(), Variant::String("tetst".to_string()))
]
},
participation_count: 99,
application_buffer: vec![1,2,3,4,5,6,7,8,9],
attributes: vec![10,20,99,100000],
datetime: KerberosDateTime::now(),
gamemode: 111,
open_participation: false,
option0: 100,
progress_score: 1,
system_password_enabled: false,
user_password: "aaa".to_string(),
session_key: vec![91,123,5,2,1,2,4,124,4],
user_password_enabled: false,
gathering: Gathering{
minimum_participants: 1,
maximum_participants: 12,
description: "aaargh".to_string(),
flags: 100,
host_pid: 999999919,
owner_pid: 138830,
participant_policy: 1,
policy_argument: 99837,
self_gid: 129,
state: 1389488
}
}
});
static DUMMY_SER: Lazy<Vec<u8>> = Lazy::new(|| serialize_to_vec(DUMMY.deref()));
fn serialize_to_vec(r: &impl RmcSerialize) -> Vec<u8>{
let mut vec = r.to_data();
vec.unwrap()
}
fn read_struct<T: RmcSerialize>(r: &[u8]) -> T{
T::deserialize(&mut Cursor::new(r)).unwrap()
}
fn matchmake_with_param(c: &mut Criterion) {
let raw = DUMMY.deref();
let ser = DUMMY_SER.deref().as_slice();
c.bench_function("mmparam: ser", |b| b.iter(move || serialize_to_vec(black_box(raw))));
c.bench_function("mmparam: de", |b| b.iter(move || read_struct::<AutoMatchmakeParam>(black_box(ser))));
}
criterion_group!(benches, matchmake_with_param);
criterion_main!(benches);

View file

@ -0,0 +1,47 @@
use once_cell::sync::Lazy;
use rnex_core::common::setup;
use rnex_core::executables::common::{SECURE_SERVER_ACCOUNT, new_simple_backend};
use rnex_core::nex::auth_handler::AuthHandler;
use rnex_core::reggie::EdgeNodeHolderConnectOption::DontRegister;
use rnex_core::reggie::RemoteEdgeNodeHolder;
use rnex_core::rmc::protocols::{OnlyRemote, new_rmc_gateway_connection};
use rnex_core::rmc::structures::RmcSerialize;
use rnex_core::util::SplittableBufferConnection;
use std::env;
use std::net::SocketAddrV4;
use std::sync::Arc;
use tokio::net::TcpStream;
pub static FORWARD_EDGE_NODE_HOLDER: Lazy<SocketAddrV4> = Lazy::new(|| {
env::var("FORWARD_EDGE_NODE_HOLDER")
.ok()
.and_then(|s| Some(s.parse().unwrap()))
.expect("FORWARD_EDGE_NODE_HOLDER not set")
});
#[tokio::main]
async fn main() {
setup();
let conn = TcpStream::connect(&*FORWARD_EDGE_NODE_HOLDER)
.await
.unwrap();
let conn: SplittableBufferConnection = conn.into();
conn.send(DontRegister.to_data().unwrap()).await;
let conn = new_rmc_gateway_connection(conn, |r| {
Arc::new(OnlyRemote::<RemoteEdgeNodeHolder>::new(r))
});
new_simple_backend(move |_, _| {
let controller = conn.clone();
Arc::new(AuthHandler {
destination_server_acct: &SECURE_SERVER_ACCOUNT,
build_name: env!("AUTH_REPORT_VERSION"),
control_server: controller,
})
})
.await;
}

View file

@ -0,0 +1,23 @@
use cfg_if::cfg_if;
use rnex_core::common::setup;
use rnex_core::executables::common::new_simple_backend;
use rnex_core::executables::friends_backend::start_friends_backend;
use rnex_core::nex::matchmake::MatchmakeManager;
use rnex_core::nex::remote_console::RemoteConsole;
use rnex_core::nex::user::User;
use rnex_core::rmc::protocols::{RemoteDisconnectable, RmcPureRemoteObject};
use std::sync::Arc;
use std::sync::atomic::AtomicU32;
#[tokio::main]
async fn main() {
setup();
cfg_if! {
if #[cfg(feature = "friends")]{
start_friends_backend().await;
} else {
regular_backend::start_regular_backend().await
}
}
}

View file

@ -0,0 +1,88 @@
use once_cell::sync::Lazy;
use rnex_core::nex::account::Account;
use rnex_core::rmc::protocols::{RmcCallable, RmcConnection, new_rmc_gateway_connection};
use rnex_core::rmc::structures::RmcSerialize;
use rnex_core::rnex_proxy_common::ConnectionInitData;
use std::env;
use std::io::Cursor;
use std::net::{Ipv4Addr, SocketAddrV4};
use std::sync::Arc;
use tokio::net::TcpListener;
use log::error;
use std::error::Error;
use crate::reggie::UnitPacketRead;
const IP_REQ_SERVICE_URL: &str = "https://ipinfo.io/ip";
pub fn try_get_ip() -> Result<Ipv4Addr, Box<dyn Error>> {
let mut req = ureq::get(IP_REQ_SERVICE_URL).call()?;
Ok(req.body_mut().read_to_string()?.parse()?)
}
pub static OWN_IP_PRIVATE: Lazy<Ipv4Addr> = Lazy::new(|| {
env::var("SERVER_IP")
.ok()
.map(|s| s.parse().expect("invalid ip address"))
.unwrap_or(Ipv4Addr::UNSPECIFIED)
});
pub static OWN_IP_PUBLIC: Lazy<Ipv4Addr> = Lazy::new(|| {
env::var("SERVER_IP_PUBLIC")
.ok()
.map(|s| s.parse().expect("invalid ip address"))
.unwrap_or_else(|| try_get_ip().unwrap())
});
pub static SERVER_PORT: Lazy<u16> = Lazy::new(|| {
env::var("SERVER_PORT")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(10000)
});
pub static KERBEROS_SERVER_PASSWORD: Lazy<String> = Lazy::new(|| {
env::var("AUTH_SERVER_PASSWORD")
.ok()
.unwrap_or("password".to_owned())
});
pub static AUTH_SERVER_ACCOUNT: Lazy<Account> =
Lazy::new(|| Account::new(1, "Quazal Authentication", &KERBEROS_SERVER_PASSWORD));
pub static SECURE_SERVER_ACCOUNT: Lazy<Account> =
Lazy::new(|| Account::new(2, "Quazal Rendez-Vous", &KERBEROS_SERVER_PASSWORD));
pub async fn new_simple_backend<T: RmcCallable + Sync + Send + 'static, F>(mut creation_function: F)
where
F: FnMut(ConnectionInitData, RmcConnection) -> Arc<T>,
{
let listen = TcpListener::bind(SocketAddrV4::new(*OWN_IP_PRIVATE, *SERVER_PORT))
.await
.unwrap();
while let Ok((mut stream, _addr)) = listen.accept().await {
let buffer = match stream.read_buffer().await {
Ok(v) => v,
Err(e) => {
error!(
"an error ocurred whilest reading connection data buffer: {:?}",
e
);
continue;
}
};
let user_connection_data = ConnectionInitData::deserialize(&mut Cursor::new(buffer));
let user_connection_data = match user_connection_data {
Ok(v) => v,
Err(e) => {
error!("an error ocurred whilest reading connection data: {:?}", e);
continue;
}
};
let fun_ref = &mut creation_function;
new_rmc_gateway_connection(stream.into(), move |r| fun_ref(user_connection_data, r));
}
}

View file

@ -1,17 +1,16 @@
use std::io::Cursor; use std::io::Cursor;
use std::net::SocketAddrV4; use std::net::SocketAddrV4;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use log::error;
use macros::rmc_struct; use macros::rmc_struct;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use rust_nex::common::setup; use rnex_core::common::setup;
use rust_nex::executables::common::{OWN_IP_PRIVATE, SERVER_PORT}; use rnex_core::executables::common::{OWN_IP_PRIVATE, SERVER_PORT};
use rust_nex::reggie::{EdgeNodeHolderConnectOption, EdgeNodeManagement, LocalEdgeNodeHolder}; use rnex_core::reggie::{EdgeNodeHolderConnectOption, EdgeNodeManagement, LocalEdgeNodeHolder};
use rust_nex::rmc::protocols::new_rmc_gateway_connection; use rnex_core::rmc::protocols::new_rmc_gateway_connection;
use rust_nex::rmc::response::ErrorCode; use rnex_core::rmc::response::ErrorCode;
use rust_nex::util::SplittableBufferConnection; use rnex_core::util::SplittableBufferConnection;
use rust_nex::rmc::structures::RmcSerialize; use rnex_core::rmc::structures::RmcSerialize;
#[rmc_struct(EdgeNodeHolder)] #[rmc_struct(EdgeNodeHolder)]
struct EdgeNode{ struct EdgeNode{
@ -56,7 +55,7 @@ async fn main() {
let holder: Arc<DataHolder> = Default::default(); let holder: Arc<DataHolder> = Default::default();
while let Ok((mut stream, addr)) = listen.accept().await { while let Ok((stream, _addr)) = listen.accept().await {
let mut conn: SplittableBufferConnection = stream.into(); let mut conn: SplittableBufferConnection = stream.into();
let Some(data) = conn.recv().await else { let Some(data) = conn.recv().await else {

View file

@ -0,0 +1,22 @@
use std::sync::{Arc, atomic::AtomicU32};
use crate::{
executables::common::new_simple_backend,
nex::friends_handler::{FriendsManager, FriendsUser},
};
pub async fn start_friends_backend() {
let fm = Arc::new(FriendsManager {
cid_counter: AtomicU32::new(1),
});
new_simple_backend(move |c, r| {
let fm = fm.clone();
Arc::new_cyclic(move |this| FriendsUser {
fm,
addr: c.prudpsock_addr,
pid: c.pid,
})
})
.await;
}

View file

@ -0,0 +1,3 @@
pub mod common;
pub mod friends_backend;
pub mod regular_backend;

View file

@ -0,0 +1,33 @@
use std::sync::{Arc, atomic::AtomicU32};
use crate::{
executables::common::new_simple_backend,
nex::{matchmake::MatchmakeManager, remote_console::RemoteConsole, user::User},
rmc::protocols::RmcPureRemoteObject,
};
pub async fn start_regular_backend() {
let mmm = Arc::new(MatchmakeManager {
//gid_counter: AtomicU32::new(1),
sessions: Default::default(),
users: Default::default(),
rv_cid_counter: AtomicU32::new(1),
});
let weak_mmm = Arc::downgrade(&mmm);
MatchmakeManager::initialize_garbage_collect_thread(weak_mmm).await;
new_simple_backend(move |c, r| {
let mmm = mmm.clone();
Arc::new_cyclic(move |this| User {
this: this.clone(),
ip: c.prudpsock_addr,
pid: c.pid,
remote: RemoteConsole::new(r),
matchmake_manager: mmm,
station_url: Default::default(),
})
})
.await;
}

View file

@ -1,11 +1,10 @@
use std::{env, result}; use std::{env, result};
use std::array::TryFromSliceError; use std::array::TryFromSliceError;
use std::str::FromStr; use std::ops::Deref;
use json::{object, JsonValue}; use json::{object, JsonValue};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use reqwest::{Body, Method, Url};
use reqwest::header::HeaderValue;
use thiserror::Error; use thiserror::Error;
use tokio::task::{spawn_blocking, JoinError};
use crate::grpc::account::Error::SomethingHappened; use crate::grpc::account::Error::SomethingHappened;
static API_KEY: Lazy<String> = Lazy::new(||{ static API_KEY: Lazy<String> = Lazy::new(||{
let key = env::var("ACCOUNT_GQL_API_KEY") let key = env::var("ACCOUNT_GQL_API_KEY")
@ -26,27 +25,38 @@ static CLIENT_URI: Lazy<String> = Lazy::new(||{
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum Error{ pub enum Error{
#[error(transparent)] #[error(transparent)]
Creation(#[from] reqwest::Error), RequestError(#[from] ureq::Error),
#[error(transparent)] #[error(transparent)]
Json(#[from] json::Error), Json(#[from] json::Error),
#[error(transparent)] //#[error(transparent)]
Status(#[from] tonic::Status), //Status(#[from] tonic::Status),
#[error("invalid password size: {0}")] #[error("invalid password size: {0}")]
PasswordConversion(#[from] TryFromSliceError), PasswordConversion(#[from] TryFromSliceError),
#[error("something happened")] #[error("something happened")]
SomethingHappened SomethingHappened,
#[error("error joining blocking task: {0}")]
Join(#[from] JoinError)
} }
pub type Result<T> = result::Result<T, Error>; pub type Result<T> = result::Result<T, Error>;
pub struct Client(reqwest::Client); pub struct Client;//(reqwest::Client);
impl Client{ impl Client{
pub async fn new() -> Result<Self> { pub async fn new() -> Result<Self> {
Ok(Self(reqwest::ClientBuilder::new().build()?)) //Ok(Self(reqwest::ClientBuilder::new().build()?))
Ok(Self)
} }
async fn do_request(&self, request_data: JsonValue) -> Result<JsonValue>{ async fn do_request(&self, request_data: JsonValue) -> Result<JsonValue>{
let request = ureq::post(CLIENT_URI.as_str())
.header("X-API-Key", API_KEY.deref())
.content_type("application/json");
let mut response = spawn_blocking(move || request.send(request_data.to_string())).await??;
let str_body = response.body_mut().read_to_string()?;
Ok(json::parse(&str_body)?)
/*
let mut request = reqwest::Request::new(Method::POST, Url::from_str(CLIENT_URI.as_str()).unwrap()); let mut request = reqwest::Request::new(Method::POST, Url::from_str(CLIENT_URI.as_str()).unwrap());
*(request.body_mut()) = Some(Body::from(request_data.to_string())); *(request.body_mut()) = Some(Body::from(request_data.to_string()));
@ -56,6 +66,8 @@ impl Client{
let response = self.0.execute(request).await?; let response = self.0.execute(request).await?;
Ok(json::parse(&response.text().await?)?) Ok(json::parse(&response.text().await?)?)
*/
} }
pub async fn get_nex_password(&mut self , pid: u32) -> Result<[u8; 16]>{ pub async fn get_nex_password(&mut self , pid: u32) -> Result<[u8; 16]>{
@ -136,21 +148,4 @@ impl Client{
Ok(response) Ok(response)
} }
} }
*/ */
#[cfg(test)]
mod test{
use crate::grpc::account::Client;
#[tokio::test]
async fn test(){
dotenv::dotenv().ok();
let mut client = Client::new().await.unwrap();
let cli = client.get_nex_password(1699562916).await.unwrap();
println!("{:?}", cli);
}
}

View file

@ -2,8 +2,7 @@
//! before account rs is finished. //! before account rs is finished.
//! //!
//! This WILL be deprecated as soon as account rs is in a stable state. //! This WILL be deprecated as soon as account rs is in a stable state.
use tonic::{Request, Status}; //use tonic::{Request, Status};
type InterceptorFunc = Box<(dyn Fn(Request<()>) -> Result<Request<()>, Status> + Send)>; //type InterceptorFunc = Box<dyn Fn(Request<()>) -> Result<Request<()>, Status> + Send>;
mod protobufs;
pub mod account; pub mod account;

View file

@ -1,23 +1,41 @@
use std::io::{Read, Write}; use bytemuck::{Pod, Zeroable, bytes_of};
use bytemuck::{bytes_of, Pod, Zeroable}; use cfg_if::cfg_if;
use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc}; use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc};
use hmac::Hmac; use hmac::Hmac;
use hmac::Mac;
use md5::{Digest, Md5}; use md5::{Digest, Md5};
use rc4::{Rc4, Rc4Core, StreamCipher}; use rc4::KeyInit;
use rc4::cipher::StreamCipherCoreWrapper; use rc4::cipher::StreamCipherCoreWrapper;
use rc4::consts::U16; use rc4::consts::U16;
use hmac::Mac; use rc4::{Rc4, Rc4Core, StreamCipher};
use rc4::KeyInit; use rnex_core::rmc::structures::RmcSerialize;
use crate::rmc::structures::RmcSerialize; use std::io::{Read, Write};
use typenum::Unsigned;
use rnex_core::rmc::structures::Result;
cfg_if! {
if #[cfg(feature = "friends")]{
pub type SESSION_KEY_LENGTH_TY = U16;
} else {
pub type SESSION_KEY_LENGTH_TY = U32;
}
}
pub const SESSION_KEY_LENGTH: usize = SESSION_KEY_LENGTH_TY::USIZE;
type Md5Hmac = Hmac<md5::Md5>; type Md5Hmac = Hmac<md5::Md5>;
pub fn derive_key(pid: u32, password: [u8; 16]) -> [u8; 16]{ pub fn derive_key(pid: u32, password: &[u8]) -> [u8; 16] {
let iteration_count = 65000 + pid%1024; let iteration_count = 65000 + pid % 1024;
// we do one iteration out here to ensure the key is always 16 bytes
let mut key = password; let mut key: [u8; 16] = {
let mut md5 = Md5::new();
md5.update(password);
md5.finalize().try_into().unwrap()
};
for _ in 0..iteration_count { for _ in 1..iteration_count {
let mut md5 = Md5::new(); let mut md5 = Md5::new();
md5.update(key); md5.update(key);
key = md5.finalize().try_into().unwrap(); key = md5.finalize().try_into().unwrap();
@ -29,12 +47,12 @@ pub fn derive_key(pid: u32, password: [u8; 16]) -> [u8; 16]{
#[repr(transparent)] #[repr(transparent)]
pub struct KerberosDateTime(pub u64); pub struct KerberosDateTime(pub u64);
impl KerberosDateTime{ impl KerberosDateTime {
pub fn new(second: u64, minute: u64, hour: u64, day: u64, month: u64, year:u64 ) -> Self { pub fn new(second: u64, minute: u64, hour: u64, day: u64, month: u64, year: u64) -> Self {
Self(second | (minute << 6) | (hour << 12) | (day << 17) | (month << 22) | (year << 26)) Self(second | (minute << 6) | (hour << 12) | (day << 17) | (month << 22) | (year << 26))
} }
pub fn now() -> Self{ pub fn now() -> Self {
let now = chrono::Utc::now(); let now = chrono::Utc::now();
Self::new( Self::new(
now.second() as u64, now.second() as u64,
@ -47,69 +65,80 @@ impl KerberosDateTime{
} }
#[inline] #[inline]
pub fn get_seconds(&self) -> u8{ pub fn get_seconds(&self) -> u8 {
(self.0 & 0b111111) as u8 (self.0 & 0b111111) as u8
} }
#[inline] #[inline]
pub fn get_minutes(&self) -> u8{ pub fn get_minutes(&self) -> u8 {
((self.0 >> 6) & 0b111111) as u8 ((self.0 >> 6) & 0b111111) as u8
} }
#[inline] #[inline]
pub fn get_hours(&self) -> u8{ pub fn get_hours(&self) -> u8 {
((self.0 >> 12) & 0b111111) as u8 ((self.0 >> 12) & 0b111111) as u8
} }
#[inline] #[inline]
pub fn get_days(&self) -> u8{ pub fn get_days(&self) -> u8 {
((self.0 >> 17) & 0b111111) as u8 ((self.0 >> 17) & 0b111111) as u8
} }
#[inline] #[inline]
pub fn get_month(&self) -> u8{ pub fn get_month(&self) -> u8 {
((self.0 >> 22) & 0b1111) as u8 ((self.0 >> 22) & 0b1111) as u8
} }
#[inline] #[inline]
pub fn get_year(&self) -> u64{ pub fn get_year(&self) -> u64 {
(self.0 >> 26) & 0xFFFFFFFF (self.0 >> 26) & 0xFFFFFFFF
} }
pub fn to_regular_time(&self) -> chrono::DateTime<Utc>{ pub fn to_regular_time(&self) -> chrono::DateTime<Utc> {
NaiveDateTime::new( NaiveDateTime::new(
NaiveDate::from_ymd_opt(self.get_year() as i32, self.get_month() as u32, self.get_days() as u32).unwrap(), NaiveDate::from_ymd_opt(
NaiveTime::from_hms_opt(self.get_hours() as u32, self.get_minutes() as u32, self.get_seconds() as u32).unwrap() self.get_year() as i32,
).and_utc() self.get_month() as u32,
self.get_days() as u32,
)
.unwrap(),
NaiveTime::from_hms_opt(
self.get_hours() as u32,
self.get_minutes() as u32,
self.get_seconds() as u32,
)
.unwrap(),
)
.and_utc()
} }
} }
impl RmcSerialize for KerberosDateTime{ impl RmcSerialize for KerberosDateTime {
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { fn serialize(&self, writer: &mut impl Write) -> Result<()> {
Ok(self.0.serialize(writer)?) Ok(self.0.serialize(writer)?)
} }
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> { fn deserialize(reader: &mut impl Read) -> Result<Self> {
Ok(Self(u64::deserialize(reader)?)) Ok(Self(u64::deserialize(reader)?))
} }
} }
#[derive(Pod, Zeroable, Copy, Clone)] #[derive(Pod, Zeroable, Copy, Clone)]
#[repr(C, packed)] #[repr(C, packed)]
pub struct TicketInternalData{ pub struct TicketInternalData {
pub issued_time: KerberosDateTime, pub issued_time: KerberosDateTime,
pub pid: u32, pub pid: u32,
pub session_key: [u8; 32], pub session_key: [u8; SESSION_KEY_LENGTH],
} }
impl TicketInternalData{ impl TicketInternalData {
pub(crate) fn new(pid: u32) -> Self{ pub(crate) fn new(pid: u32) -> Self {
Self{ Self {
issued_time: KerberosDateTime::now(), issued_time: KerberosDateTime::now(),
pid, pid,
session_key: rand::random() session_key: rand::random(),
} }
} }
pub(crate) fn encrypt(&self, key: [u8; 16]) -> Box<[u8]>{ pub(crate) fn encrypt(&self, key: [u8; 16]) -> Box<[u8]> {
let mut data = bytes_of(self).to_vec(); let mut data = bytes_of(self).to_vec();
let mut rc4: StreamCipherCoreWrapper<Rc4Core<U16>> = Rc4::new_from_slice(&key).unwrap(); let mut rc4: StreamCipherCoreWrapper<Rc4Core<U16>> = Rc4::new_from_slice(&key).unwrap();
@ -117,11 +146,13 @@ impl TicketInternalData{
let mut hmac = <Md5Hmac as KeyInit>::new_from_slice(&key).unwrap(); let mut hmac = <Md5Hmac as KeyInit>::new_from_slice(&key).unwrap();
hmac.write_all(&data[..]).expect("failed to write data to hmac"); hmac.write_all(&data[..])
.expect("failed to write data to hmac");
let hmac_result = &hmac.finalize().into_bytes()[..]; let hmac_result = &hmac.finalize().into_bytes()[..];
data.write_all(&hmac_result).expect("failed to write data to vec"); data.write_all(&hmac_result)
.expect("failed to write data to vec");
data.into_boxed_slice() data.into_boxed_slice()
} }
@ -129,42 +160,44 @@ impl TicketInternalData{
#[derive(Pod, Zeroable, Copy, Clone)] #[derive(Pod, Zeroable, Copy, Clone)]
#[repr(C, packed)] #[repr(C, packed)]
pub struct Ticket{ pub struct Ticket {
pub session_key: [u8; 32], pub session_key: [u8; SESSION_KEY_LENGTH],
pub pid: u32, pub pid: u32,
} }
impl Ticket{ impl Ticket {
pub(crate) fn encrypt(&self, key: [u8; 16], internal_data: &[u8]) -> Box<[u8]>{ pub fn encrypt(&self, key: [u8; 16], internal_data: &[u8]) -> Box<[u8]> {
let mut data = bytes_of(self).to_vec(); let mut data = bytes_of(self).to_vec();
internal_data.serialize(&mut data).expect("unable to write to vec"); internal_data
.serialize(&mut data)
.expect("unable to write to vec");
let mut rc4: StreamCipherCoreWrapper<Rc4Core<U16>> = Rc4::new_from_slice(&key).unwrap(); let mut rc4: StreamCipherCoreWrapper<Rc4Core<U16>> = Rc4::new_from_slice(&key).unwrap();
rc4.apply_keystream(&mut data); rc4.apply_keystream(&mut data);
let mut hmac = <Md5Hmac as KeyInit>::new_from_slice(&key).unwrap(); let mut hmac = <Md5Hmac as KeyInit>::new_from_slice(&key).unwrap();
hmac.write_all(&data[..]).expect("failed to write data to hmac"); hmac.write_all(&data[..])
.expect("failed to write data to hmac");
let hmac_result = &hmac.finalize().into_bytes()[..]; let hmac_result = &hmac.finalize().into_bytes()[..];
data.write_all(&hmac_result).expect("failed to write data to vec"); data.write_all(&hmac_result)
.expect("failed to write data to vec");
data.into_boxed_slice() data.into_boxed_slice()
} }
} }
#[cfg(test)] #[cfg(test)]
mod test{ mod test {
use chrono::{Datelike, Utc};
use crate::kerberos::KerberosDateTime; use crate::kerberos::KerberosDateTime;
#[test] #[test]
fn kerberos_time_convert_test(){ fn kerberos_time_convert_test() {
let time = KerberosDateTime(135904948834); let time = KerberosDateTime(135904948834);
println!("{}", time.to_regular_time().to_rfc2822()); println!("{}", time.to_regular_time().to_rfc2822());
} }
} }

View file

@ -6,9 +6,8 @@
extern crate self as rust_nex; extern crate self as rnex_core;
pub mod endianness;
pub mod prudp; pub mod prudp;
pub mod rmc; pub mod rmc;
//mod protocols; //mod protocols;
@ -18,9 +17,13 @@ pub mod kerberos;
pub mod nex; pub mod nex;
pub mod result; pub mod result;
pub mod versions; pub mod versions;
pub mod web;
pub mod common; pub mod common;
pub mod reggie; pub mod reggie;
pub mod rnex_proxy_common; pub mod rnex_proxy_common;
pub mod util; pub mod util;
pub mod executables; pub mod executables;
pub use macros::*;
pub mod config{
pub const FEATURE_HAS_STRUCT_HEADER: bool = cfg!(feature = "rmc_struct_header");
}

127
rnex-core/src/main.rs Normal file
View file

@ -0,0 +1,127 @@
#![allow(dead_code)]
#![allow(async_fn_in_trait)]
//#![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.
extern crate self as rust_nex;
use once_cell::sync::Lazy;
use std::hint::black_box;
mod prudp;
pub mod rmc;
//mod protocols;
mod grpc;
mod kerberos;
mod nex;
mod result;
mod versions;
pub mod reggie;
pub mod util;
pub mod common;
pub mod config{
pub const FEATURE_HAS_STRUCT_HEADER: bool = cfg!(feature = "rmc_struct_header");
}
use std::io::Cursor;
use std::ops::Deref;
use rnex_core::kerberos::KerberosDateTime;
use rnex_core::rmc::structures::matchmake::{AutoMatchmakeParam, Gathering, MatchmakeParam, MatchmakeSession, MatchmakeSessionSearchCriteria};
use rnex_core::rmc::structures::RmcSerialize;
use rnex_core::rmc::structures::variant::Variant;
static DUMMY: Lazy<AutoMatchmakeParam> = Lazy::new(|| AutoMatchmakeParam{
additional_participants: vec![1,2,3,4],
auto_matchmake_option: 10,
gid_for_participation_check: 9,
join_message: "hi".to_string(),
participation_count: 32,
target_gids: vec![45,2,51,1,1,1,1],
search_criteria: vec![MatchmakeSessionSearchCriteria{
attribs: vec!["hi".to_string(), "ig".to_string(), "gotta put data here".to_string()],
exclude_locked: true,
exclude_non_host_pid: false,
exclude_system_password_set: true,
exclude_user_password_set: false,
game_mode: "some gamemode".to_string(),
matchmake_param: MatchmakeParam{
params: vec![
("SR".to_string(), Variant::Bool(true)),
("SR2".to_string(), Variant::Double(1.0)),
("SR3".to_string(), Variant::SInt64(42)),
("SR4".to_string(), Variant::String("test".to_string()))
]
},
matchmake_system_type: "some type".to_string(),
maximum_participants: "???".to_string(),
minimum_participants: "-99".to_string(),
refer_gid: 123,
selection_method: 9999999,
vacant_only: true,
vacant_participants: 1000
}],
matchmake_session: MatchmakeSession{
refer_gid: 10,
matchmake_system_type: 139,
matchmake_param: MatchmakeParam{
params: vec![
("QSR".to_string(), Variant::Bool(false)),
("SRQ2".to_string(), Variant::Double(1.1)),
("SQR3".to_string(), Variant::SInt64(422)),
("SDR4".to_string(), Variant::String("tetst".to_string()))
]
},
participation_count: 99,
application_buffer: vec![1,2,3,4,5,6,7,8,9],
attributes: vec![10,20,99,100000],
datetime: KerberosDateTime::now(),
gamemode: 111,
open_participation: false,
option0: 100,
progress_score: 1,
system_password_enabled: false,
user_password: "aaa".to_string(),
session_key: vec![91,123,5,2,1,2,4,124,4],
user_password_enabled: false,
gathering: Gathering{
minimum_participants: 1,
maximum_participants: 12,
description: "aaargh".to_string(),
flags: 100,
host_pid: 999999919,
owner_pid: 138830,
participant_policy: 1,
policy_argument: 99837,
self_gid: 129,
state: 1389488
}
}
});
static DUMMY_SER: Lazy<Vec<u8>> = Lazy::new(|| serialize_to_vec(DUMMY.deref()));
fn serialize_to_vec(r: &impl RmcSerialize) -> Vec<u8>{
let vec = r.to_data();
vec.unwrap()
}
fn read_struct<T: RmcSerialize>(r: &[u8]) -> T{
T::deserialize(&mut Cursor::new(r)).unwrap()
}
fn main(){
for _ in 0..10000000 {
let v = serialize_to_vec(black_box(DUMMY.deref()));
let u = read_struct::<AutoMatchmakeParam>(black_box(DUMMY_SER.deref().as_slice()));
black_box(v);
black_box(u);
}
}

View file

@ -0,0 +1,32 @@
use macros::RmcSerialize;
#[derive(RmcSerialize, Clone)]
pub struct Account {
pub pid: u32,
pub username: String,
pub kerbros_password: Box<[u8]>,
}
impl Account {
pub fn new(pid: u32, username: &str, passwd: &str) -> Self {
let passwd_data = passwd.as_bytes();
Self {
kerbros_password: passwd.as_bytes().into(),
username: username.into(),
pid,
}
}
pub fn new_raw_password(pid: u32, username: &str, passwd: &[u8]) -> Self {
Self {
kerbros_password: passwd.into(),
username: username.into(),
pid,
}
}
pub fn get_login_data(&self) -> (u32, &[u8]) {
(self.pid, &self.kerbros_password)
}
}

View file

@ -1,19 +1,22 @@
use crate::grpc::account;
use crate::reggie::{RemoteEdgeNodeHolder, RemoteEdgeNodeManagement};
use crate::{define_rmc_proto, kerberos};
use cfg_if::cfg_if;
use log::{info, warn};
use macros::rmc_struct;
use rnex_core::kerberos::{KerberosDateTime, Ticket, derive_key};
use rnex_core::nex::account::Account;
use rnex_core::rmc::protocols::OnlyRemote;
use rnex_core::rmc::protocols::auth::{Auth, RawAuth, RawAuthInfo, RemoteAuth};
use rnex_core::rmc::response::ErrorCode;
use rnex_core::rmc::response::ErrorCode::Core_Unknown;
use rnex_core::rmc::structures::any::Any;
use rnex_core::rmc::structures::connection_data::ConnectionData;
use rnex_core::rmc::structures::connection_data::ConnectionDataOld;
use rnex_core::rmc::structures::qresult::QResult;
use std::hash::{DefaultHasher, Hasher}; use std::hash::{DefaultHasher, Hasher};
use std::net::SocketAddrV4; use std::net::SocketAddrV4;
use std::sync::Arc; use std::sync::{Arc, LazyLock, OnceLock};
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::{define_rmc_proto, kerberos};
use macros::rmc_struct;
use crate::reggie::{RemoteEdgeNodeHolder, RemoteEdgeNodeManagement};
use crate::rmc::protocols::OnlyRemote;
define_rmc_proto!( define_rmc_proto!(
proto AuthClientProtocol{ proto AuthClientProtocol{
@ -30,8 +33,8 @@ pub struct AuthHandler {
} }
pub fn generate_ticket( pub fn generate_ticket(
source_act_login_data: (u32, [u8; 16]), source_act_login_data: (u32, &[u8]),
dest_act_login_data: (u32, [u8; 16]), dest_act_login_data: (u32, &[u8]),
) -> Box<[u8]> { ) -> Box<[u8]> {
let source_key = derive_key(source_act_login_data.0, source_act_login_data.1); 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 dest_key = derive_key(dest_act_login_data.0, dest_act_login_data.1);
@ -48,7 +51,13 @@ pub fn generate_ticket(
encrypted_session_ticket encrypted_session_ticket
} }
async fn get_login_data_by_pid(pid: u32) -> Option<(u32, [u8; 16])> { async fn get_login_data_by_pid(pid: u32) -> Option<(u32, Box<[u8]>)> {
if pid == GUEST_ACCOUNT.pid {
let source_login_data = GUEST_ACCOUNT.get_login_data();
return Some((source_login_data.0, source_login_data.1.into()));
}
let Ok(mut client) = account::Client::new().await else { let Ok(mut client) = account::Client::new().await else {
return None; return None;
}; };
@ -57,19 +66,96 @@ async fn get_login_data_by_pid(pid: u32) -> Option<(u32, [u8; 16])> {
return None; return None;
}; };
Some((pid, passwd)) Some((pid, passwd.into()))
} }
fn station_url_from_sock_addr(sock_addr: SocketAddrV4) -> String{ fn station_url_from_sock_addr(sock_addr: SocketAddrV4) -> String {
format!( format!(
"prudps:/PID=2;sid=1;stream=10;type=2;address={};port={};CID=1", "prudps:/PID=2;sid=1;stream=10;type=2;address={};port={};CID=1",
sock_addr.ip(), sock_addr.port() sock_addr.ip(),
sock_addr.port()
) )
} }
static GUEST_ACCOUNT: LazyLock<Account> =
LazyLock::new(|| Account::new(100, "guest", "MMQea3n!fsik"));
impl AuthHandler {
pub async fn generate_ticket_from_name(
&self,
name: &str,
) -> Result<(u32, Box<[u8]>), ErrorCode> {
#[cfg(feature = "guest_login")]
{
if name == GUEST_ACCOUNT.username {
let source_login_data = GUEST_ACCOUNT.get_login_data();
let destination_login_data = self.destination_server_acct.get_login_data();
return Ok((
source_login_data.0,
generate_ticket(source_login_data, destination_login_data),
));
}
}
let Ok(pid) = name.parse() else {
warn!("unable to connect to parse pid: {}", name);
return Err(ErrorCode::Core_InvalidArgument);
};
let Ok(mut client) = account::Client::new().await else {
warn!("unable to connect to grpc");
return Err(ErrorCode::Core_Exception);
};
let Ok(passwd) = client.get_nex_password(pid).await else {
warn!("unable to get nex password");
return Err(ErrorCode::Core_Exception);
};
let source_login_data = (pid, &passwd[..]);
let destination_login_data = self.destination_server_acct.get_login_data();
Ok((
pid,
generate_ticket(source_login_data, destination_login_data),
))
}
}
impl Auth for AuthHandler { impl Auth for AuthHandler {
async fn login(&self, _name: String) -> Result<(), ErrorCode> { async fn login(
todo!() &self,
name: String,
) -> Result<(QResult, u32, Vec<u8>, ConnectionDataOld, String), ErrorCode> {
let (pid, ticket) = self.generate_ticket_from_name(&name).await?;
let result = QResult::success(Core_Unknown);
let mut hasher = DefaultHasher::new();
hasher.write(name.as_bytes());
let Ok(addr) = self.control_server.get_url(hasher.finish()).await else {
warn!("no secure proxies");
return Err(ErrorCode::Core_Exception);
};
let connection_data = ConnectionDataOld {
station_url: station_url_from_sock_addr(addr),
special_station_url: "".to_string(),
special_protocols: Vec::new(),
};
let ret = (
result,
pid,
ticket.into(),
connection_data,
self.build_name.to_string(),
);
info!("data: {:?}", ret);
Ok(ret)
} }
async fn login_ex( async fn login_ex(
@ -77,30 +163,16 @@ impl Auth for AuthHandler {
name: String, name: String,
_extra_data: Any, _extra_data: Any,
) -> Result<(QResult, u32, Vec<u8>, ConnectionData, String), ErrorCode> { ) -> Result<(QResult, u32, Vec<u8>, ConnectionData, String), ErrorCode> {
let Ok(pid) = name.parse() else { let (pid, ticket) = self.generate_ticket_from_name(&name).await?;
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 result = QResult::success(Core_Unknown);
let mut hasher = DefaultHasher::new(); let mut hasher = DefaultHasher::new();
hasher.write(name.as_bytes()); hasher.write(name.as_bytes());
let Ok(addr) = self.control_server.get_url(hasher.finish()).await else { let Ok(addr) = self.control_server.get_url(hasher.finish()).await else {
warn!("no secure proxies");
return Err(ErrorCode::Core_Exception); return Err(ErrorCode::Core_Exception);
}; };
@ -112,13 +184,16 @@ impl Auth for AuthHandler {
special_protocols: Vec::new(), special_protocols: Vec::new(),
}; };
Ok(( let ret = (
result, result,
source_login_data.0, pid,
ticket.into(), ticket.into(),
connection_data, connection_data,
self.build_name.to_string() //format!("{}; Rust NEX Version {} by DJMrTV", self.build_name, env!("CARGO_PKG_VERSION")), self.build_name.to_string(),
)) );
info!("data: {:?}", ret);
Ok(ret)
} }
async fn request_ticket( async fn request_ticket(
@ -126,22 +201,19 @@ impl Auth for AuthHandler {
source_pid: u32, source_pid: u32,
destination_pid: u32, destination_pid: u32,
) -> Result<(QResult, Vec<u8>), ErrorCode> { ) -> Result<(QResult, Vec<u8>), ErrorCode> {
let Some(source_login_data) = get_login_data_by_pid(source_pid).await else { let Some((pid, passwd)) = get_login_data_by_pid(source_pid).await else {
return Err(ErrorCode::Core_Exception); return Err(ErrorCode::Core_Exception);
}; };
let desgination_login_data = if destination_pid == self.destination_server_acct.pid { let desgination_login_data = if destination_pid == self.destination_server_acct.pid {
self.destination_server_acct.get_login_data() self.destination_server_acct.get_login_data()
} else { } else {
let Some(login) = get_login_data_by_pid(destination_pid).await else { return Err(ErrorCode::RendezVous_InvalidOperation);
return Err(ErrorCode::Core_Exception);
};
login
}; };
let result = QResult::success(Core_Unknown); let result = QResult::success(Core_Unknown);
let ticket = generate_ticket(source_login_data, desgination_login_data); let ticket = generate_ticket((pid, &passwd[..]), desgination_login_data);
Ok((result, ticket.into())) Ok((result, ticket.into()))
} }
@ -157,30 +229,30 @@ impl Auth for AuthHandler {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::rmc::structures::connection_data::ConnectionData; use rnex_core::rmc::response::RMCResponse;
use crate::rmc::structures::qresult::QResult; use rnex_core::rmc::structures::RmcSerialize;
use crate::rmc::structures::RmcSerialize; use rnex_core::rmc::structures::connection_data::ConnectionData;
use crate::rmc::response::RMCResponse; use rnex_core::rmc::structures::qresult::QResult;
use std::io::Cursor; use std::io::Cursor;
#[test] #[test]
fn test() { fn test() {
return;
let stuff = hex::decode("200100000a0106000000028000000100010051b3995774000000a6321c7f78847c1c5e9fb825eb26bd91841f1a40d92fc694159666119cb13527f1463ac48ad42a63e6613ede67041554b1770978112e6f1f3e177a2bfc75933216dbe38f70133a1eb28e2ae32a4b5c4b0c3e3efd4c02907992e259b257270b57a9dbe7792f4721b07f8fafb9e32d50f2555c616a015c0000004b007072756470733a2f5049443d323b7369643d313b73747265616d3d31303b747970653d323b616464726573733d322e3234332e39352e3131333b706f72743d31303030313b4349443d3100000000000100002c153ba51f00000033006272616e63683a6f726967696e2f70726f6a6563742f7775702d61676d6a206275696c643a335f385f31355f323030345f3000").unwrap(); let stuff = hex::decode("200100000a0106000000028000000100010051b3995774000000a6321c7f78847c1c5e9fb825eb26bd91841f1a40d92fc694159666119cb13527f1463ac48ad42a63e6613ede67041554b1770978112e6f1f3e177a2bfc75933216dbe38f70133a1eb28e2ae32a4b5c4b0c3e3efd4c02907992e259b257270b57a9dbe7792f4721b07f8fafb9e32d50f2555c616a015c0000004b007072756470733a2f5049443d323b7369643d313b73747265616d3d31303b747970653d323b616464726573733d322e3234332e39352e3131333b706f72743d31303030313b4349443d3100000000000100002c153ba51f00000033006272616e63683a6f726967696e2f70726f6a6563742f7775702d61676d6a206275696c643a335f385f31355f323030345f3000").unwrap();
let stuff = RMCResponse::new(&mut Cursor::new(stuff)).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 { let rnex_core::rmc::response::RMCResponseResult::Success { .. } = stuff.response_result
else {
panic!() panic!()
}; };
// let stuff = hex::decode("0100010051B399577400000085F1736FCFBE93660275A3FE36FED6C2EFC57222AC99A9219CF54170A415B02DF1463AC48AD42A6307813FDE67041554B177097832ED000F892D9551A09F88E9CB0388DC1BC9527CC7384556A3287B2A349ABBF7E34A5A3EC14C2287CC7F78DA616BC3B03A035347FBD2E9A505C8EF42447CD809015F0000004E007072756470733A2F73747265616D3D31303B747970653D323B616464726573733D3139322E3136382E3137382E3132303B706F72743D31303030313B4349443D313B5049443D323B7369643D310000000000010000CDF53AA51F00000033006272616E63683A6F726967696E2F70726F6A6563742F7775702D61676D6A206275696C643A335F385F31355F323030345F3000").unwrap(); // let stuff = hex::decode("0100010051B399577400000085F1736FCFBE93660275A3FE36FED6C2EFC57222AC99A9219CF54170A415B02DF1463AC48AD42A6307813FDE67041554B177097832ED000F892D9551A09F88E9CB0388DC1BC9527CC7384556A3287B2A349ABBF7E34A5A3EC14C2287CC7F78DA616BC3B03A035347FBD2E9A505C8EF42447CD809015F0000004E007072756470733A2F73747265616D3D31303B747970653D323B616464726573733D3139322E3136382E3137382E3132303B706F72743D31303030313B4349443D313B5049443D323B7369643D310000000000010000CDF53AA51F00000033006272616E63683A6F726967696E2F70726F6A6563742F7775702D61676D6A206275696C643A335F385F31355F323030345F3000").unwrap();
// let stuff = hex::decode("0100010051b399577400000037d3d4814d2b16dd546c94a75d32637b45f856b5abe73cf26cfaa235c5f2c1cef1463ac48ad42a637d873fde67041554b177097880cfa7e10bb810eaf686bfb0a0cf3d65b1f476ebc046d0855327986f557dca14fbb8594883c186b863f2206f22baa0309dbcc81da2f883cb2cdc12628ec7fced015c0000004b007072756470733a2f5049443d323b7369643d313b73747265616d3d31303b747970653d323b616464726573733d322e3234332e39352e3131333b706f72743d31303030313b4349443d310000000000010000b7f33aa51f00000033006272616e63683a6f726967696e2f70726f6a6563742f7775702d61676d6a206275696c643a335f385f31355f323030345f3000").unwrap(); let stuff = hex::decode("0100010051b399577400000037d3d4814d2b16dd546c94a75d32637b45f856b5abe73cf26cfaa235c5f2c1cef1463ac48ad42a637d873fde67041554b177097880cfa7e10bb810eaf686bfb0a0cf3d65b1f476ebc046d0855327986f557dca14fbb8594883c186b863f2206f22baa0309dbcc81da2f883cb2cdc12628ec7fced015c0000004b007072756470733a2f5049443d323b7369643d313b73747265616d3d31303b747970653d323b616464726573733d322e3234332e39352e3131333b706f72743d31303030313b4349443d310000000000010000b7f33aa51f00000033006272616e63683a6f726967696e2f70726f6a6563742f7775702d61676d6a206275696c643a335f385f31355f323030345f3000").unwrap();
let data = <(QResult, u32, Vec<u8>, ConnectionData, String) as RmcSerialize>::deserialize( let data = <(QResult, u32, Vec<u8>, ConnectionData, String) as RmcSerialize>::deserialize(
&mut Cursor::new(stuff), &mut Cursor::new(stuff),
).unwrap(); )
.unwrap();
println!("data: {:?}", data); println!("data: {:?}", data);
} }

View file

@ -0,0 +1,93 @@
use rnex_core::prudp::station_url::StationUrl;
use rnex_core::prudp::station_url::UrlOptions::{
Address, NatFiltering, NatMapping, NatType, Port, PrincipalID, RVConnectionID,
};
use rnex_core::prudp::station_url::nat_types::PUBLIC;
use rnex_core::rmc::response::ErrorCode::Core_Exception;
use rnex_core::prudp::socket_addr::PRUDPSockAddr;
use rnex_core::rmc::response::ErrorCode;
pub async fn get_station_urls(
station_urls: &[StationUrl],
addr: PRUDPSockAddr,
pid: u32,
cid: u32,
) -> Result<Vec<StationUrl>, ErrorCode> {
let mut public_station: Option<StationUrl> = None;
let mut private_station: Option<StationUrl> = None;
for station in station_urls {
let is_public = station.options.iter().any(|v| {
if let NatType(v) = v {
if *v & PUBLIC != 0 {
return true;
}
}
false
});
let Some(nat_filtering) = station.options.iter().find_map(|v| match v {
NatFiltering(v) => Some(v),
_ => None,
}) else {
return Err(Core_Exception);
};
let Some(nat_mapping) = station.options.iter().find_map(|v| match v {
NatMapping(v) => Some(v),
_ => None,
}) else {
return Err(Core_Exception);
};
if !is_public || (*nat_filtering == 0 && *nat_mapping == 0) {
private_station = Some(station.clone());
}
if is_public {
public_station = Some(station.clone());
}
}
let Some(mut private_station) = private_station else {
return Err(Core_Exception);
};
let mut public_station = if let Some(public_station) = public_station {
public_station
} else {
let mut public_station = private_station.clone();
public_station.options.retain(|v| match v {
Address(_) | Port(_) | NatFiltering(_) | NatMapping(_) | NatType(_) => false,
_ => true,
});
public_station
.options
.push(Address(*addr.regular_socket_addr.ip()));
public_station
.options
.push(Port(addr.regular_socket_addr.port()));
public_station.options.push(NatFiltering(0));
public_station.options.push(NatMapping(0));
public_station.options.push(NatType(3));
public_station
};
let both = [&mut public_station, &mut private_station];
for station in both {
station.options.retain(|v| match v {
PrincipalID(_) | RVConnectionID(_) => false,
_ => true,
});
station.options.push(PrincipalID(pid));
station.options.push(RVConnectionID(cid));
}
Ok(vec![public_station])
}

View file

@ -0,0 +1,160 @@
use std::sync::{Arc, atomic::AtomicU32};
use std::time::Duration;
use log::info;
use macros::rmc_struct;
use rnex_core::rmc::protocols::friends::{Friends, RawFriends, RawFriendsInfo, RemoteFriends};
use rnex_core::rmc::protocols::secure::{RawSecure, RawSecureInfo, RemoteSecure, Secure};
use rnex_core::{
define_rmc_proto,
kerberos::KerberosDateTime,
nex::common::get_station_urls,
prudp::{socket_addr::PRUDPSockAddr, station_url::StationUrl},
rmc::{
protocols::friends::{
BlacklistedPrincipal, Comment, FriendInfo, FriendRequest, NNAInfo, NintendoPresenceV2,
PersistentNotification, PrincipalPreference,
},
response::ErrorCode,
structures::{any::Any, qresult::QResult},
},
};
use std::sync::atomic::Ordering::Relaxed;
use tokio::time::sleep;
use rnex_core::rmc::protocols::friends::{GameKey, MiiV2, PrincipalBasicInfo};
define_rmc_proto!(
proto FriendsUser{
Secure,
Friends
}
);
#[rmc_struct(FriendsUser)]
pub struct FriendsUser {
pub fm: Arc<FriendsManager>,
pub addr: PRUDPSockAddr,
pub pid: u32,
}
pub struct FriendsManager {
pub cid_counter: AtomicU32,
}
impl FriendsManager {
pub fn next_cid(&self) -> u32 {
self.cid_counter.fetch_add(1, Relaxed)
}
}
impl Friends for FriendsUser {
async fn update_and_get_all_information(
&self,
info: NNAInfo,
presence: NintendoPresenceV2,
date_time: KerberosDateTime,
) -> Result<
(
PrincipalPreference,
Comment,
Vec<FriendInfo>,
Vec<FriendRequest>,
Vec<FriendRequest>,
Vec<BlacklistedPrincipal>,
bool,
Vec<PersistentNotification>,
bool,
),
ErrorCode,
> {
Ok((
PrincipalPreference {
block_friend_request: false,
show_online: false,
show_playing_title: false,
},
Comment {
last_changed: KerberosDateTime::now(),
message: "".to_string(),
unk: 0,
},
vec![FriendInfo {
became_friends: KerberosDateTime::now(),
comment: Comment {
last_changed: KerberosDateTime::now(),
message: "I'm just a dummy account :3".to_string(),
unk: 0,
},
last_online: KerberosDateTime::now(),
nna_info: NNAInfo {
principal_basic_info: PrincipalBasicInfo {
pid: 101,
nnid: "dummyaccount".to_string(),
mii: MiiV2{
date_time: KerberosDateTime::now(),
name: "Dummy Account".to_string(),
mii_data: hex::decode("030000402bd7c32986a771f2dc6b35e31da15e37ff7c0000391e6f006f006d0069000000000000000000000000004040001065033568641e2013661a611821640f0000290052485000000000000000000000000000000000000000000000e838").unwrap(),
unk: 0,
unk2: 0,
},
unk: 0
},
unk: 0,
unk2: 0
},
presence: NintendoPresenceV2{
changed_flags: 0,
message: "".to_string(),
app_data: vec![],
game_key: GameKey{
tid: 0x00050002101ce400,
version: 0x0
},
game_server_id: 0,
is_online: true,
gid: 0,
pid: 101,
unk: 0,
unk2: 0,
unk3: 0,
unk4: 0,
unk5: 0,
unk6: 0,
unk7: 0
},
unk: 0
}],
vec![],
vec![],
vec![],
false,
vec![],
false,
))
}
}
impl Secure for FriendsUser {
async fn register(
&self,
station_urls: Vec<StationUrl>,
) -> Result<(QResult, u32, StationUrl), ErrorCode> {
let cid = self.fm.next_cid();
Ok((
QResult::success(ErrorCode::Core_Unknown),
cid,
get_station_urls(&station_urls, self.addr, self.pid, cid).await?[0].clone(),
))
}
async fn register_ex(
&self,
station_urls: Vec<StationUrl>,
data: Any,
) -> Result<(QResult, u32, StationUrl), ErrorCode> {
info!("register");
self.register(station_urls).await
}
async fn replace_url(&self, target: StationUrl, dest: StationUrl) -> Result<(), ErrorCode> {
Err(ErrorCode::Core_NotImplemented)
}
}

View file

@ -0,0 +1,481 @@
use crate::nex::user::User;
use crate::rmc::protocols::notifications::{NotificationEvent, RemoteNotification};
use log::info;
use rand::random;
use rnex_core::kerberos::KerberosDateTime;
use rnex_core::rmc::protocols::notifications::notification_types::{
HOST_CHANGED, OWNERSHIP_CHANGED,
};
use rnex_core::rmc::response::ErrorCode;
use rnex_core::rmc::response::ErrorCode::{Core_InvalidArgument, RendezVous_SessionVoid};
use rnex_core::rmc::structures::matchmake::gathering_flags::PERSISTENT_GATHERING;
use rnex_core::rmc::structures::matchmake::{
Gathering, MatchmakeParam, MatchmakeSession, MatchmakeSessionSearchCriteria,
};
use rnex_core::rmc::structures::variant::Variant;
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::atomic::AtomicU32;
use std::sync::atomic::Ordering::Relaxed;
use std::sync::{Arc, Weak};
use std::time::Duration;
use tokio::sync::{Mutex, RwLock};
use tokio::time::sleep;
pub struct MatchmakeManager {
//pub gid_counter: AtomicU32,
pub sessions: RwLock<HashMap<u32, Arc<Mutex<ExtendedMatchmakeSession>>>>,
pub rv_cid_counter: AtomicU32,
pub users: RwLock<HashMap<u32, Weak<User>>>,
}
impl MatchmakeManager {
pub fn next_gid(&self) -> u32 {
random()
//self.gid_counter.fetch_add(1, Relaxed)
}
pub fn next_cid(&self) -> u32 {
self.rv_cid_counter.fetch_add(1, Relaxed)
}
pub async fn get_session(
&self,
gid: u32,
) -> Result<Arc<Mutex<ExtendedMatchmakeSession>>, ErrorCode> {
let sessions = self.sessions.read().await;
let Some(session) = sessions.get(&gid) else {
return Err(RendezVous_SessionVoid);
};
let session = session.clone();
drop(sessions);
Ok(session)
}
async fn garbage_collect(&self) {
info!("running rnex garbage collector over all sessions and users");
let mut idx = 0;
let mut to_be_deleted_gids = Vec::new();
// i am very well aware of how inefficient doing it like this is but this is the only
// way which i could think of to do this without potentially causing a deadlock of
// the entire server
while let Some((gid, session)) = {
let sessions = self.sessions.read().await;
let session_pair = sessions.iter().nth(idx).map(|s| (*s.0, s.1.clone()));
drop(sessions);
session_pair
} {
let session = session.lock().await;
if !session.is_reachable() {
to_be_deleted_gids.push(gid);
}
idx += 1;
}
let mut sessions = self.sessions.write().await;
for gid in to_be_deleted_gids {
sessions.remove(&gid);
}
}
pub async fn initialize_garbage_collect_thread(this: Weak<Self>) {
tokio::spawn(async move {
while let Some(this) = this.upgrade() {
this.garbage_collect().await;
// every 30 minutes
sleep(Duration::from_secs(60 * 30)).await;
}
});
}
}
#[derive(Default, Debug)]
pub struct ExtendedMatchmakeSession {
pub session: MatchmakeSession,
pub connected_players: Vec<Weak<User>>,
}
fn read_bounds_string<T: FromStr>(str: &str) -> Option<(T, T)> {
let bounds = str.split_once(",")?;
Some((T::from_str(bounds.0).ok()?, T::from_str(bounds.1).ok()?))
}
fn check_bounds_str<T: FromStr + PartialOrd>(compare: T, str: &str) -> Option<bool> {
let bounds: (T, T) = read_bounds_string(str)?;
Some(bounds.0 <= compare && compare <= bounds.1)
}
pub async fn broadcast_notification<T: AsRef<User>>(
players: &[T],
notification_event: &NotificationEvent,
) {
for player in players {
let player = player.as_ref();
player
.remote
.process_notification_event(notification_event.clone())
.await;
}
}
impl ExtendedMatchmakeSession {
#[inline(always)]
pub fn get_active_players(&self) -> Vec<Arc<User>> {
self.connected_players
.iter()
.filter_map(|u| u.upgrade())
.collect()
}
#[inline(always)]
pub async fn broadcast_notification(&self, notification_event: &NotificationEvent) {
broadcast_notification(&self.get_active_players(), notification_event).await;
}
pub async fn from_matchmake_session(
gid: u32,
session: MatchmakeSession,
host: &Weak<User>,
) -> Self {
let Some(host) = host.upgrade() else {
return Default::default();
};
let mm_session = MatchmakeSession {
gathering: Gathering {
self_gid: gid,
owner_pid: host.pid,
host_pid: host.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_players(&mut self, conns: &[Weak<User>], join_msg: String) {
let Some(initiating_user) = conns[0].upgrade() else {
return;
};
let initiating_pid = initiating_user.pid;
let old_particip = self.connected_players.clone();
for conn in conns {
self.connected_players.push(conn.clone());
}
self.session.participation_count = self.connected_players.len() as u32;
for other_connection in &conns[1..] {
let Some(other_conn) = other_connection.upgrade() else {
continue;
};
let other_pid = other_conn.pid;
/*if other_pid == self.session.gathering.owner_pid &&
joining_pid == self.session.gathering.owner_pid{
continue;
}*/
other_conn
.remote
.process_notification_event(NotificationEvent {
pid_source: initiating_pid,
notif_type: 122000,
param_1: self.session.gathering.self_gid,
param_2: other_pid,
str_param: "".into(),
param_3: 0,
})
.await;
}
let list_of_connected_pids: Vec<_> = self
.connected_players
.iter()
.filter_map(|p| p.upgrade())
.map(|p| p.pid)
.collect();
for other_connection in conns {
let Some(other_conn) = other_connection.upgrade() else {
continue;
};
// let other_pid = other_conn.pid;
/*if other_pid == self.session.gathering.owner_pid &&
joining_pid == self.session.gathering.owner_pid{
continue;
}*/
for pid in &list_of_connected_pids {
other_conn
.remote
.process_notification_event(NotificationEvent {
pid_source: initiating_pid,
notif_type: 3001,
param_1: self.session.gathering.self_gid,
param_2: *pid,
str_param: join_msg.clone(),
param_3: self.connected_players.len() as _,
})
.await;
}
}
for old_conns in &old_particip {
let Some(old_conns) = old_conns.upgrade() else {
continue;
};
let older_pid = old_conns.pid;
initiating_user
.remote
.process_notification_event(NotificationEvent {
pid_source: initiating_pid,
notif_type: 3001,
param_1: self.session.gathering.self_gid,
param_2: older_pid,
str_param: join_msg.clone(),
param_3: self.connected_players.len() as _,
})
.await;
}
}
pub fn has_active_players(&self) -> bool {
self.connected_players
.iter()
.filter(|v| v.upgrade().is_some())
.count()
!= 0
}
#[inline]
pub fn is_reachable(&self) -> bool {
(if self.session.gathering.flags & PERSISTENT_GATHERING != 0 {
if self.has_active_players() {
true
} else {
self.session.open_participation
}
} else {
self.has_active_players()
}) & self.has_active_players()
}
#[inline]
pub fn is_joinable(&self) -> bool {
self.is_reachable() && self.session.open_participation
}
pub fn matches_criteria(
&self,
search_criteria: &MatchmakeSessionSearchCriteria,
) -> Result<bool, ErrorCode> {
// todo: implement the rest of the search criteria
if search_criteria.vacant_only {
if (self.connected_players.len() as u16 + search_criteria.vacant_participants)
> self.session.gathering.maximum_participants
{
return Ok(false);
}
}
if search_criteria.exclude_locked {
if !self.session.open_participation {
return Ok(false);
}
}
if search_criteria.exclude_system_password_set {
if self.session.system_password_enabled {
return Ok(false);
}
}
if search_criteria.exclude_user_password_set {
if self.session.user_password_enabled {
return Ok(false);
}
}
if !check_bounds_str(
self.session.gathering.minimum_participants,
&search_criteria.minimum_participants,
)
.ok_or(Core_InvalidArgument)?
{
return Ok(false);
}
if !check_bounds_str(
self.session.gathering.maximum_participants,
&search_criteria.maximum_participants,
)
.ok_or(Core_InvalidArgument)?
{
return Ok(false);
}
let game_mode: u32 = search_criteria
.game_mode
.parse()
.map_err(|_| Core_InvalidArgument)?;
if self.session.gamemode != game_mode {
return Ok(false);
}
let mm_sys_type: u32 = search_criteria
.matchmake_system_type
.parse()
.map_err(|_| Core_InvalidArgument)?;
if self.session.matchmake_system_type != mm_sys_type {
return Ok(false);
}
if search_criteria
.attribs
.get(0)
.map(|str| str.parse().ok())
.flatten()
!= self.session.attributes.get(0).map(|v| *v)
{
return Ok(false);
}
if search_criteria
.attribs
.get(2)
.map(|str| str.parse().ok())
.flatten()
!= self.session.attributes.get(2).map(|v| *v)
{
return Ok(false);
}
if search_criteria
.attribs
.get(3)
.map(|str| str.parse().ok())
.flatten()
!= self.session.attributes.get(3).map(|v| *v)
{
return Ok(false);
}
Ok(true)
}
pub async fn migrate_ownership(&mut self, initiator_pid: u32) -> Result<(), ErrorCode> {
let players: Vec<_> = self
.connected_players
.iter()
.filter_map(|p| p.upgrade())
.collect();
let Some(new_owner) = players
.iter()
.find(|p| p.pid != self.session.gathering.owner_pid)
else {
self.session.gathering.owner_pid = 0;
return Ok(());
};
self.session.gathering.owner_pid = new_owner.pid;
self.broadcast_notification(&NotificationEvent {
pid_source: initiator_pid,
notif_type: OWNERSHIP_CHANGED,
param_1: self.session.gathering.self_gid,
param_2: new_owner.pid,
..Default::default()
})
.await;
Ok(())
}
pub async fn migrate_host(&mut self, initiator_pid: u32) -> Result<(), ErrorCode> {
// let players: Vec<_> = self.connected_players.iter().filter_map(|p| p.upgrade()).collect();
self.session.gathering.host_pid = self.session.gathering.owner_pid;
self.broadcast_notification(&NotificationEvent {
pid_source: initiator_pid,
notif_type: HOST_CHANGED,
param_1: self.session.gathering.self_gid,
..Default::default()
})
.await;
Ok(())
}
pub async fn remove_player_from_session(
&mut self,
pid: u32,
message: &str,
) -> Result<(), ErrorCode> {
self.connected_players
.retain(|u| u.upgrade().is_some_and(|u| u.pid != pid));
self.session.participation_count =
(self.connected_players.len() & u32::MAX as usize) as u32;
if pid == self.session.gathering.owner_pid {
self.migrate_ownership(pid).await?;
}
if pid == self.session.gathering.host_pid {
self.migrate_host(pid).await?;
}
// todo: support DisconnectChangeOwner
// todo: finish the rest of this
for player in self.connected_players.iter().filter_map(|p| p.upgrade()) {
player
.remote
.process_notification_event(NotificationEvent {
notif_type: 3008,
pid_source: pid,
param_1: self.session.gathering.self_gid,
param_2: pid,
str_param: message.to_owned(),
..Default::default()
})
.await;
}
Ok(())
}
}

View file

@ -1,5 +1,7 @@
pub mod account; pub mod account;
pub mod auth_handler; pub mod auth_handler;
pub mod user; pub mod common;
pub mod friends_handler;
pub mod matchmake;
pub mod remote_console; pub mod remote_console;
pub mod matchmake; pub mod user;

View file

@ -1,33 +1,43 @@
use crate::define_rmc_proto; use crate::define_rmc_proto;
use crate::nex::common::get_station_urls;
use crate::nex::matchmake::{ExtendedMatchmakeSession, MatchmakeManager}; use crate::nex::matchmake::{ExtendedMatchmakeSession, MatchmakeManager};
use crate::nex::remote_console::RemoteConsole; use crate::nex::remote_console::RemoteConsole;
use crate::prudp::sockaddr::PRUDPSockAddr;
use crate::prudp::station_url::UrlOptions::{
Address, NatFiltering, NatMapping, NatType, Port, PrincipalID, RVConnectionID,
};
use crate::prudp::station_url::{StationUrl};
use crate::rmc::protocols::matchmake::{ use crate::rmc::protocols::matchmake::{
Matchmake, RawMatchmake, RawMatchmakeInfo, RemoteMatchmake, Matchmake, RawMatchmake, RawMatchmakeInfo, RemoteMatchmake,
}; };
use crate::rmc::protocols::ranking::{Ranking, RawRanking, RawRankingInfo, RemoteRanking}; use crate::rmc::protocols::nat_traversal::{
use crate::rmc::protocols::matchmake_extension::{ NatTraversal, RawNatTraversal, RawNatTraversalInfo, RemoteNatTraversal,
RemoteNatTraversalConsole,
};
use rnex_core::prudp::station_url::StationUrl;
use rnex_core::prudp::station_url::UrlOptions::{
Address, NatFiltering, NatMapping, NatType, Port, PrincipalID, RVConnectionID,
};
use rnex_core::rmc::protocols::matchmake_ext::{
MatchmakeExt, RawMatchmakeExt, RawMatchmakeExtInfo, RemoteMatchmakeExt,
};
use rnex_core::rmc::protocols::matchmake_extension::{
MatchmakeExtension, RawMatchmakeExtension, RawMatchmakeExtensionInfo, RemoteMatchmakeExtension, MatchmakeExtension, RawMatchmakeExtension, RawMatchmakeExtensionInfo, RemoteMatchmakeExtension,
}; };
use crate::rmc::protocols::nat_traversal::{NatTraversal, RawNatTraversal, RawNatTraversalInfo, RemoteNatTraversal, RemoteNatTraversalConsole}; use rnex_core::rmc::protocols::ranking::{Ranking, RawRanking, RawRankingInfo, RemoteRanking};
use crate::rmc::protocols::secure::{RawSecure, RawSecureInfo, RemoteSecure, Secure}; use rnex_core::rmc::protocols::secure::{RawSecure, RawSecureInfo, RemoteSecure, Secure};
use crate::rmc::protocols::matchmake_ext::{MatchmakeExt, RawMatchmakeExt, RawMatchmakeExtInfo, RemoteMatchmakeExt}; use rnex_core::rmc::response::ErrorCode;
use crate::rmc::response::ErrorCode; use rnex_core::rmc::structures::any::Any;
use crate::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession}; use rnex_core::rmc::structures::matchmake::{
AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession,
};
use crate::rmc::structures::qresult::QResult;
use macros::rmc_struct;
use std::sync::{Arc, Weak};
use log::info;
use tokio::sync::{Mutex, RwLock};
use crate::prudp::station_url::nat_types::PUBLIC;
use crate::rmc::protocols::notifications::{NotificationEvent, RemoteNotification}; use crate::rmc::protocols::notifications::{NotificationEvent, RemoteNotification};
use crate::rmc::response::ErrorCode::{Core_Exception, Core_InvalidArgument, RendezVous_AccountExpired}; use log::info;
use macros::rmc_struct;
use rnex_core::prudp::socket_addr::PRUDPSockAddr;
use rnex_core::prudp::station_url::nat_types::PUBLIC;
use rnex_core::rmc::response::ErrorCode::{
Core_Exception, Core_InvalidArgument, RendezVous_AccountExpired,
};
use rnex_core::rmc::structures::qresult::QResult;
use std::sync::{Arc, Weak};
use tokio::sync::{Mutex, RwLock};
define_rmc_proto!( define_rmc_proto!(
proto UserProtocol{ proto UserProtocol{
@ -63,98 +73,27 @@ impl Secure for User {
users.insert(cid, self.this.clone()); users.insert(cid, self.this.clone());
drop(users); drop(users);
let mut public_station: Option<StationUrl> = None; let stations = get_station_urls(&station_urls, self.ip, self.pid, cid).await?;
let mut private_station: Option<StationUrl> = None;
for station in station_urls {
let is_public = station.options.iter().any(|v| {
if let NatType(v) = v {
if *v & PUBLIC != 0 {
return true;
}
}
false
});
let Some(nat_filtering) = station.options.iter().find_map(|v| match v {
NatFiltering(v) => Some(v),
_ => None
}) else {
return Err(Core_Exception);
};
let Some(nat_mapping) = station.options.iter().find_map(|v| match v {
NatMapping(v) => Some(v),
_ => None
}) else {
return Err(Core_Exception);
};
if !is_public || (*nat_filtering == 0 && *nat_mapping == 0) {
private_station = Some(station.clone());
}
if is_public {
public_station = Some(station);
}
}
let Some(mut private_station) = private_station else {
return Err(Core_Exception);
};
let mut public_station = if let Some(public_station) = public_station {
public_station
} else {
let mut public_station = private_station.clone();
public_station.options.retain(|v| {
match v {
Address(_) | Port(_) | NatFiltering(_) | NatMapping(_) | NatType(_) => false,
_ => true
}
});
public_station.options.push(Address(*self.ip.regular_socket_addr.ip()));
public_station.options.push(Port(self.ip.regular_socket_addr.port()));
public_station.options.push(NatFiltering(0));
public_station.options.push(NatMapping(0));
public_station.options.push(NatType(3));
public_station
};
let both = [&mut public_station, &mut private_station];
for station in both {
station.options.retain(|v| {
match v {
PrincipalID(_) | RVConnectionID(_) => false,
_ => true
}
});
station.options.push(PrincipalID(self.pid));
station.options.push(RVConnectionID(cid));
}
let first = stations.first().unwrap().clone();
let mut lock = self.station_url.write().await; let mut lock = self.station_url.write().await;
*lock = vec![ *lock = stations;
public_station.clone(),
// private_station.clone()
];
drop(lock); drop(lock);
let result = QResult::success(ErrorCode::Core_Unknown); let result = QResult::success(ErrorCode::Core_Unknown);
let out = public_station.to_string(); Ok((result, cid, first))
}
println!("out: {}", out); async fn register_ex(
&self,
Ok((result, cid, public_station)) station_urls: Vec<StationUrl>,
_data: Any,
) -> Result<(QResult, u32, StationUrl), ErrorCode> {
self.register(station_urls).await
} }
async fn replace_url(&self, target_url: StationUrl, dest: StationUrl) -> Result<(), ErrorCode> { async fn replace_url(&self, target_url: StationUrl, dest: StationUrl) -> Result<(), ErrorCode> {
@ -169,8 +108,8 @@ impl Secure for User {
}; };
let Some(replacement_target) = lock.iter_mut().find(|url| { let Some(replacement_target) = lock.iter_mut().find(|url| {
url.options.iter().any(|o| o == target_addr) && url.options.iter().any(|o| o == target_addr)
url.options.iter().any(|o| o == target_port) && url.options.iter().any(|o| o == target_port)
}) else { }) else {
return Err(ErrorCode::Core_InvalidArgument); return Err(ErrorCode::Core_InvalidArgument);
}; };
@ -230,22 +169,34 @@ impl MatchmakeExtension for User {
create_session_param.matchmake_session, create_session_param.matchmake_session,
&self.this.clone(), &self.this.clone(),
) )
.await; .await;
let mut joining_players = vec![self.this.clone()]; let mut joining_players = vec![self.this.clone()];
let users = self.matchmake_manager.users.read().await; let users = self.matchmake_manager.users.read().await;
if let Ok(old_gathering) = self.matchmake_manager.get_session(create_session_param.gid_for_participation_check).await { if let Ok(old_gathering) = self
.matchmake_manager
.get_session(create_session_param.gid_for_participation_check)
.await
{
let old_gathering = old_gathering.lock().await; let old_gathering = old_gathering.lock().await;
let players = old_gathering.connected_players.iter().filter_map(|v| v.upgrade()).filter(|u| create_session_param.additional_participants.iter().any(|p| *p == u.pid)); let players = old_gathering
.connected_players
.iter()
.filter_map(|v| v.upgrade())
.filter(|u| {
create_session_param
.additional_participants
.iter()
.any(|p| *p == u.pid)
});
for player in players { for player in players {
joining_players.push(Arc::downgrade(&player)); joining_players.push(Arc::downgrade(&player));
} }
} }
drop(users); drop(users);
new_session.session.participation_count = create_session_param.participation_count as u32; new_session.session.participation_count = create_session_param.participation_count as u32;
@ -266,20 +217,43 @@ impl MatchmakeExtension for User {
&self, &self,
join_session_param: JoinMatchmakeSessionParam, join_session_param: JoinMatchmakeSessionParam,
) -> Result<MatchmakeSession, ErrorCode> { ) -> Result<MatchmakeSession, ErrorCode> {
let session = self.matchmake_manager.get_session(join_session_param.gid).await?; let session = self
.matchmake_manager
.get_session(join_session_param.gid)
.await?;
let mut session = session.lock().await; let mut session = session.lock().await;
if session.session.user_password_enabled {
if join_session_param.user_password != session.session.user_password {
return Err(ErrorCode::RendezVous_InvalidPassword);
}
}
session.connected_players.retain(|v| v.upgrade().is_some_and(|v| v.pid != self.pid)); session
.connected_players
.retain(|v| v.upgrade().is_some_and(|v| v.pid != self.pid));
let mut joining_players = vec![self.this.clone()]; let mut joining_players = vec![self.this.clone()];
let users = self.matchmake_manager.users.read().await; let users = self.matchmake_manager.users.read().await;
if let Ok(old_gathering) = self.matchmake_manager.get_session(join_session_param.gid_for_participation_check).await { if let Ok(old_gathering) = self
.matchmake_manager
.get_session(join_session_param.gid_for_participation_check)
.await
{
let old_gathering = old_gathering.lock().await; let old_gathering = old_gathering.lock().await;
let players = old_gathering.connected_players.iter().filter_map(|v| v.upgrade()).filter(|u| join_session_param.additional_participants.iter().any(|p| *p == u.pid)); let players = old_gathering
.connected_players
.iter()
.filter_map(|v| v.upgrade())
.filter(|u| {
join_session_param
.additional_participants
.iter()
.any(|p| *p == u.pid)
});
for player in players { for player in players {
joining_players.push(Arc::downgrade(&player)); joining_players.push(Arc::downgrade(&player));
} }
@ -296,17 +270,28 @@ impl MatchmakeExtension for User {
Ok(mm_session) Ok(mm_session)
} }
async fn auto_matchmake_with_param_postpone(&self, param: AutoMatchmakeParam) -> Result<MatchmakeSession, ErrorCode> { async fn auto_matchmake_with_param_postpone(
&self,
param: AutoMatchmakeParam,
) -> Result<MatchmakeSession, ErrorCode> {
println!("{:?}", param); println!("{:?}", param);
let mut joining_players = vec![self.this.clone()]; let mut joining_players = vec![self.this.clone()];
let users = self.matchmake_manager.users.read().await; let users = self.matchmake_manager.users.read().await;
if let Ok(old_gathering) = self.matchmake_manager.get_session(param.gid_for_participation_check).await { if let Ok(old_gathering) = self
.matchmake_manager
.get_session(param.gid_for_participation_check)
.await
{
let old_gathering = old_gathering.lock().await; let old_gathering = old_gathering.lock().await;
let players = old_gathering.connected_players.iter().filter_map(|v| v.upgrade()).filter(|u| param.additional_participants.iter().any(|p| *p == u.pid)); let players = old_gathering
.connected_players
.iter()
.filter_map(|v| v.upgrade())
.filter(|u| param.additional_participants.iter().any(|p| *p == u.pid));
for player in players { for player in players {
joining_players.push(Arc::downgrade(&player)); joining_players.push(Arc::downgrade(&player));
} }
@ -333,7 +318,9 @@ impl MatchmakeExtension for User {
} }
if bool_matched_criteria { if bool_matched_criteria {
session.add_players(&joining_players, param.join_message).await; session
.add_players(&joining_players, param.join_message)
.await;
return Ok(session.session.clone()); return Ok(session.session.clone());
} }
@ -359,17 +346,26 @@ impl MatchmakeExtension for User {
create_matchmake_session_option: 0, create_matchmake_session_option: 0,
matchmake_session, matchmake_session,
additional_participants, additional_participants,
}).await })
.await
} }
async fn find_matchmake_session_by_gathering_id_detail(&self, gid: u32) -> Result<MatchmakeSession, ErrorCode> { async fn find_matchmake_session_by_gathering_id_detail(
&self,
gid: u32,
) -> Result<MatchmakeSession, ErrorCode> {
let session = self.matchmake_manager.get_session(gid).await?; let session = self.matchmake_manager.get_session(gid).await?;
let session = session.lock().await; let session = session.lock().await;
Ok(session.session.clone()) Ok(session.session.clone())
} }
async fn modify_current_game_attribute(&self, gid: u32, attrib_index: u32, attrib_val: u32) -> Result<(), ErrorCode> { async fn modify_current_game_attribute(
&self,
gid: u32,
attrib_index: u32,
attrib_val: u32,
) -> Result<(), ErrorCode> {
let session = self.matchmake_manager.get_session(gid).await?; let session = self.matchmake_manager.get_session(gid).await?;
let mut session = session.lock().await; let mut session = session.lock().await;
@ -388,30 +384,30 @@ impl Matchmake for User {
let session = session.lock().await; let session = session.lock().await;
let urls: Vec<_> = let urls: Vec<_> = session
session .connected_players
.connected_players .iter()
.iter() .filter_map(|v| v.upgrade())
.filter_map(|v| v.upgrade()) .filter(|u| u.pid == session.session.gathering.host_pid)
.filter(|u| u.pid == session.session.gathering.host_pid) .map(|u| async move { u.station_url.read().await.clone() })
.map(|u| async move { .next()
u.station_url.read().await.clone() .ok_or(ErrorCode::RendezVous_SessionClosed)?
}) .await;
.next()
.ok_or(ErrorCode::RendezVous_SessionClosed)?
.await;
println!("{:?}", urls); println!("{:?}", urls);
if urls.is_empty(){ if urls.is_empty() {
return Err(ErrorCode::RendezVous_NotParticipatedGathering) return Err(ErrorCode::RendezVous_NotParticipatedGathering);
} }
Ok(urls) Ok(urls)
} }
async fn update_session_host(&self, gid: u32, change_session_owner: bool) -> Result<(), ErrorCode> { async fn update_session_host(
&self,
gid: u32,
change_session_owner: bool,
) -> Result<(), ErrorCode> {
let session = self.matchmake_manager.get_session(gid).await?; let session = self.matchmake_manager.get_session(gid).await?;
let mut session = session.lock().await; let mut session = session.lock().await;
@ -422,40 +418,50 @@ impl Matchmake for User {
continue; continue;
}; };
player.remote.process_notification_event(NotificationEvent { player
notif_type: 110000, .remote
pid_source: self.pid, .process_notification_event(NotificationEvent {
param_1: gid, notif_type: 110000,
param_2: self.pid, pid_source: self.pid,
param_3: 0, param_1: gid,
str_param: "".to_string(), param_2: self.pid,
}).await; param_3: 0,
str_param: "".to_string(),
})
.await;
} }
if change_session_owner { if change_session_owner {
session.session.gathering.owner_pid = self.pid; session.session.gathering.owner_pid = self.pid;
for player in &session.connected_players { for player in &session.connected_players {
let Some(player) = player.upgrade() else { let Some(player) = player.upgrade() else {
continue; continue;
}; };
player.remote.process_notification_event(NotificationEvent { player
notif_type: 4000, .remote
pid_source: self.pid, .process_notification_event(NotificationEvent {
param_1: gid, notif_type: 4000,
param_2: self.pid, pid_source: self.pid,
param_3: 0, param_1: gid,
str_param: "".to_string(), param_2: self.pid,
}).await; param_3: 0,
str_param: "".to_string(),
})
.await;
} }
} }
Ok(()) Ok(())
} }
async fn migrate_gathering_ownership(&self, gid: u32, candidates: Vec<u32>, _participants_only: bool) -> Result<(), ErrorCode> { async fn migrate_gathering_ownership(
&self,
gid: u32,
candidates: Vec<u32>,
_participants_only: bool,
) -> Result<(), ErrorCode> {
let session = self.matchmake_manager.get_session(gid).await?; let session = self.matchmake_manager.get_session(gid).await?;
let mut session = session.lock().await; let mut session = session.lock().await;
@ -468,14 +474,17 @@ impl Matchmake for User {
continue; continue;
}; };
player.remote.process_notification_event(NotificationEvent { player
notif_type: 4000, .remote
pid_source: self.pid, .process_notification_event(NotificationEvent {
param_1: gid, notif_type: 4000,
param_2: *candidate, pid_source: self.pid,
param_3: 0, param_1: gid,
str_param: "".to_string(), param_2: *candidate,
}).await; param_3: 0,
str_param: "".to_string(),
})
.await;
} }
Ok(()) Ok(())
@ -487,12 +496,12 @@ impl MatchmakeExt for User {
let session = self.matchmake_manager.get_session(gid).await?; let session = self.matchmake_manager.get_session(gid).await?;
let mut session = session.lock().await; let mut session = session.lock().await;
session.remove_player_from_session(self.pid, &message).await?; session
.remove_player_from_session(self.pid, &message)
.await?;
Ok(true) Ok(true)
} }
} }
impl NatTraversal for User { impl NatTraversal for User {
@ -507,7 +516,7 @@ impl NatTraversal for User {
for station_url in urls.iter_mut() { for station_url in urls.iter_mut() {
station_url.options.retain(|o| match o { station_url.options.retain(|o| match o {
NatMapping(_) | NatFiltering(_) => false, NatMapping(_) | NatFiltering(_) => false,
_ => true _ => true,
}); });
station_url.options.push(NatMapping(nat_mapping as u8)); station_url.options.push(NatMapping(nat_mapping as u8));
@ -517,7 +526,12 @@ impl NatTraversal for User {
Ok(()) Ok(())
} }
async fn report_nat_traversal_result(&self, _cid: u32, _result: bool, _rtt: u32) -> Result<(), ErrorCode> { async fn report_nat_traversal_result(
&self,
_cid: u32,
_result: bool,
_rtt: u32,
) -> Result<(), ErrorCode> {
Ok(()) Ok(())
} }
@ -526,17 +540,28 @@ impl NatTraversal for User {
Err(RendezVous_AccountExpired) Err(RendezVous_AccountExpired)
} }
async fn request_probe_initialization_ext(&self, target_list: Vec<String>, station_to_probe: String) -> Result<(), ErrorCode> { async fn request_probe_initialization_ext(
&self,
target_list: Vec<String>,
station_to_probe: String,
) -> Result<(), ErrorCode> {
let users = self.matchmake_manager.users.read().await; let users = self.matchmake_manager.users.read().await;
println!("requesting station probe for {:?} to {:?}", target_list, station_to_probe); println!(
"requesting station probe for {:?} to {:?}",
target_list, station_to_probe
);
for target in target_list { for target in target_list {
let Ok(url) = StationUrl::try_from(target.as_ref()) else { let Ok(url) = StationUrl::try_from(target.as_ref()) else {
continue; continue;
}; };
let Some(RVConnectionID(v)) = url.options.into_iter().find(|o| { matches!(o, &RVConnectionID(_)) }) else { let Some(RVConnectionID(v)) = url
.options
.into_iter()
.find(|o| matches!(o, &RVConnectionID(_)))
else {
continue; continue;
}; };
@ -548,7 +573,9 @@ impl NatTraversal for User {
continue; continue;
}; };
user.remote.request_probe_initiation(station_to_probe.clone()).await; user.remote
.request_probe_initiation(station_to_probe.clone())
.await;
} }
info!("finished probing"); info!("finished probing");
@ -557,6 +584,4 @@ impl NatTraversal for User {
} }
} }
impl Ranking for User{ impl Ranking for User {}
}

View file

@ -0,0 +1,20 @@
use std::sync::LazyLock;
use rc4::{Key, StreamCipher};
use typenum::U5;
pub struct EncryptionPair<T: StreamCipher + Send> {
pub send: T,
pub recv: T,
}
impl<T: StreamCipher + Send> EncryptionPair<T> {
pub fn init_both<F: Fn() -> T>(func: F) -> Self {
Self {
recv: func(),
send: func(),
}
}
}
pub static DEFAULT_KEY: LazyLock<Key<U5>> = LazyLock::new(|| Key::from(*b"CD&ML"));

View file

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

View file

@ -1,37 +1,38 @@
use hmac::Hmac;
use macros::RmcSerialize;
use md5::digest::Mac;
use rnex_core::prudp::virtual_port::VirtualPort;
use std::io::Write; use std::io::Write;
use std::net::SocketAddrV4; use std::net::SocketAddrV4;
use hmac::{Hmac, Mac};
use macros::RmcSerialize;
use crate::prudp::packet::VirtualPort;
type Md5Hmac = Hmac<md5::Md5>; type Md5Hmac = Hmac<md5::Md5>;
#[derive(Eq, PartialEq, Hash, Debug, Copy, Clone, Ord, PartialOrd, RmcSerialize)] #[derive(Eq, PartialEq, Hash, Debug, Copy, Clone, Ord, PartialOrd, RmcSerialize)]
#[rmc_struct(0)] #[rmc_struct(0)]
pub struct PRUDPSockAddr{ pub struct PRUDPSockAddr {
pub regular_socket_addr: SocketAddrV4, pub regular_socket_addr: SocketAddrV4,
pub virtual_port: VirtualPort pub virtual_port: VirtualPort,
} }
impl PRUDPSockAddr {
pub fn new(regular_socket_addr: SocketAddrV4, virtual_port: VirtualPort) -> Self {
impl PRUDPSockAddr{ Self {
pub fn new(regular_socket_addr: SocketAddrV4, virtual_port: VirtualPort) -> Self{
Self{
regular_socket_addr, regular_socket_addr,
virtual_port virtual_port,
} }
} }
pub(super) fn calculate_connection_signature(&self) -> [u8; 16] { pub fn calculate_connection_signature(&self) -> [u8; 16] {
let mut hmac = Md5Hmac::new_from_slice(&[0; 16]).expect("fuck"); let mut hmac = Md5Hmac::new_from_slice(&[0; 16]).expect("fuck");
let mut data = self.regular_socket_addr.ip().octets().to_vec(); let 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"); hmac.write_all(&data)
let result: [u8; 16] = hmac.finalize().into_bytes()[0..16].try_into().expect("fuck"); .expect("figuring this out was complete ass");
let result: [u8; 16] = hmac.finalize().into_bytes()[0..16]
.try_into()
.expect("fuck");
result result
} }
} }

View file

@ -5,6 +5,7 @@ use std::io::Read;
use crate::prudp::station_url::Type::{PRUDP, PRUDPS, UDP}; use crate::prudp::station_url::Type::{PRUDP, PRUDPS, UDP};
use crate::prudp::station_url::UrlOptions::{Address, ConnectionID, NatFiltering, NatMapping, NatType, Platform, PMP, Port, PrincipalID, RVConnectionID, StreamID, StreamType, UPNP, PID}; use crate::prudp::station_url::UrlOptions::{Address, ConnectionID, NatFiltering, NatMapping, NatType, Platform, PMP, Port, PrincipalID, RVConnectionID, StreamID, StreamType, UPNP, PID};
use crate::rmc::structures::Error::StationUrlInvalid; use crate::rmc::structures::Error::StationUrlInvalid;
use crate::rmc::structures::helpers::DummyFormatWriter;
use crate::rmc::structures::RmcSerialize; use crate::rmc::structures::RmcSerialize;
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
pub enum Type{ pub enum Type{
@ -111,7 +112,7 @@ impl StationUrl{
impl TryFrom<&str> for StationUrl{ impl TryFrom<&str> for StationUrl{
type Error = (); type Error = ();
fn try_from(value: &str) -> Result<Self, ()> { fn try_from(value: &str) -> Result<Self, ()> {
let (url_type, options) = value.split_at(value.find(":/").ok_or(())?); let (url_type, options) = value.split_at(value.find(":/").ok_or(())?);
@ -135,59 +136,65 @@ impl TryFrom<&str> for StationUrl{
} }
} }
impl<'a> Into<String> for &'a StationUrl{ impl Display for StationUrl{
fn into(self) -> String { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut url = match self.url_type{ let url_type_str = match self.url_type{
UDP => "udp:/", UDP => "udp:/",
PRUDP => "prudp:/", PRUDP => "prudp:/",
PRUDPS => "prudps:/" PRUDPS => "prudps:/"
}.to_owned(); };
write!(f, "{}",url_type_str)?;
for option in &self.options{ for option in &self.options{
match option{ match option{
Address(v) => write!(url, "address={}", v).expect("failed to write"), Address(v) => write!(f, "address={}", v)?,
Port(v) => write!(url, "port={}", v).expect("failed to write"), Port(v) => write!(f, "port={}", v)?,
StreamType(v) => write!(url, "stream={}", v).expect("failed to write"), StreamType(v) => write!(f, "stream={}", v)?,
StreamID(v) => write!(url, "sid={}", v).expect("failed to write"), StreamID(v) => write!(f, "sid={}", v)?,
ConnectionID(v) => write!(url, "CID={}", v).expect("failed to write"), ConnectionID(v) => write!(f, "CID={}", v)?,
PrincipalID(v) => write!(url, "PID={}", v).expect("failed to write"), PrincipalID(v) => write!(f, "PID={}", v)?,
NatType(v) => write!(url, "type={}", v).expect("failed to write"), NatType(v) => write!(f, "type={}", v)?,
NatMapping(v) => write!(url, "natm={}", v).expect("failed to write"), NatMapping(v) => write!(f, "natm={}", v)?,
NatFiltering(v) => write!(url, "natf={}", v).expect("failed to write"), NatFiltering(v) => write!(f, "natf={}", v)?,
UPNP(v) => write!(url, "upnp={}", v).expect("failed to write"), UPNP(v) => write!(f, "upnp={}", v)?,
RVConnectionID(v) => write!(url, "RVCID={}", v).expect("failed to write"), RVConnectionID(v) => write!(f, "RVCID={}", v)?,
Platform(v) => write!(url, "pl={}", v).expect("failed to write"), Platform(v) => write!(f, "pl={}", v)?,
PMP(v) => write!(url, "pmp={}", v).expect("failed to write"), PMP(v) => write!(f, "pmp={}", v)?,
PID(v) => write!(url, "PID={}", v).expect("failed to write"), PID(v) => write!(f, "PID={}", v)?,
} }
write!(url, ";").expect("failed to write"); write!(f, ";")?;
} }
Ok(())
}
}
impl<'a> Into<String> for &'a StationUrl{
fn into(self) -> String {
let url = self.to_string();
url[0..url.len()-1].into() url[0..url.len()-1].into()
} }
} }
impl Display for StationUrl{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let str: String = self.into();
write!(f, "{}", str)
}
}
impl RmcSerialize for StationUrl{ impl RmcSerialize for StationUrl{
fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result<Self> { fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result<Self> {
let str = String::deserialize(reader)?; let str = String::deserialize(reader)?;
Self::try_from(str.as_str()).map_err(|_| StationUrlInvalid) Self::try_from(str.as_str()).map_err(|_| StationUrlInvalid)
} }
fn serialize(&self, writer: &mut dyn std::io::Write) -> crate::rmc::structures::Result<()> { fn serialize(&self, writer: &mut impl std::io::Write) -> crate::rmc::structures::Result<()> {
let str: String = self.into(); let str: String = self.into();
str.serialize(writer) str.serialize(writer)
} }
fn serialize_write_size(&self) -> crate::rmc::structures::Result<u32> {
let mut dummy = DummyFormatWriter::new();
write!(&mut dummy, "{}", self)?;
Ok(dummy.serialize_str_len())
}
} }
impl Debug for StationUrl{ impl Debug for StationUrl{

View file

@ -0,0 +1,81 @@
use std::io::Cursor;
use log::{error, info};
use rc4::{KeyInit, Rc4, Rc4Core, StreamCipher, cipher::StreamCipherCoreWrapper};
use typenum::{U16, U32};
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
use crate::{
kerberos::{SESSION_KEY_LENGTH, SESSION_KEY_LENGTH_TY, TicketInternalData, derive_key},
nex::account::Account,
rmc::structures::RmcSerialize,
};
pub fn read_secure_connection_data(
data: &[u8],
act: &Account,
) -> Option<([u8; SESSION_KEY_LENGTH], u32, u32)> {
let mut cursor = Cursor::new(data);
let mut ticket_data: Vec<u8> = Vec::deserialize(&mut cursor).ok()?;
let mut request_data: Vec<u8> = Vec::deserialize(&mut cursor).ok()?;
info!(
"done
request data"
);
let ticket_data_size = ticket_data.len();
let ticket_data = &mut ticket_data[0..ticket_data_size - 0x10];
let server_key = derive_key(act.pid, &act.kerbros_password[..]);
let mut rc4: StreamCipherCoreWrapper<Rc4Core<U16>> =
Rc4::new_from_slice(&server_key).expect("unable to init rc4 keystream");
rc4.apply_keystream(ticket_data);
let ticket_data: &TicketInternalData = match bytemuck::try_from_bytes(ticket_data) {
Ok(v) => v,
Err(e) => {
error!("unable to read internal ticket data: {}", e);
return None;
}
};
// todo: add ticket expiration
let TicketInternalData {
session_key,
pid: ticket_source_pid,
issued_time,
} = *ticket_data;
// todo: add checking if tickets are signed with a valid md5-hmac
let request_data_length = request_data.len();
let request_data = &mut request_data[0..request_data_length - 0x10];
let mut rc4: StreamCipherCoreWrapper<Rc4Core<SESSION_KEY_LENGTH_TY>> =
Rc4::new_from_slice(&session_key).expect("unable to init rc4 keystream");
rc4.apply_keystream(request_data);
let mut reqest_data_cursor = Cursor::new(request_data);
let pid: u32 = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?;
if pid != ticket_source_pid {
let ticket_created_on = issued_time.to_regular_time();
error!(
"someone tried to spoof their pid, ticket was created on: {}",
ticket_created_on.to_rfc2822()
);
return None;
}
let _cid: u32 = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?;
let response_check: u32 = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?;
Some((session_key, pid, response_check))
}

View file

@ -0,0 +1,64 @@
use std::fmt::{Debug, Formatter};
use bytemuck::{Pod, Zeroable};
use v_byte_helpers::SwapEndian;
#[repr(transparent)]
#[derive(Copy, Clone, Pod, Zeroable, SwapEndian, Default, Eq, PartialEq)]
pub struct TypesFlags(pub u16);
impl TypesFlags {
#[inline]
pub const fn get_types(self) -> u8 {
(self.0 & 0x000F) as u8
}
#[inline]
pub const fn get_flags(self) -> u16 {
(self.0 & 0xFFF0) >> 4
}
#[inline]
pub const fn types(self, val: u8) -> Self {
Self((self.0 & 0xFFF0) | (val as u16 & 0x000F))
}
#[inline]
pub const fn flags(self, val: u16) -> Self {
Self((self.0 & 0x000F) | ((val << 4) & 0xFFF0))
}
#[inline]
pub const fn set_flag(&mut self, val: u16) {
self.0 |= (val & 0xFFF) << 4;
}
#[inline]
pub const fn set_types(&mut self, val: u8) {
self.0 |= val as u16 & 0x0F;
}
}
impl Debug for TypesFlags {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let stream_type = self.get_types();
let port_number = self.get_flags();
write!(
f,
"TypesFlags{{ types: {}, flags: {} }}",
stream_type, port_number
)
}
}
pub mod flags {
pub const ACK: u16 = 0x001;
pub const RELIABLE: u16 = 0x002;
pub const NEED_ACK: u16 = 0x004;
pub const HAS_SIZE: u16 = 0x008;
pub const MULTI_ACK: u16 = 0x200;
}
pub mod types {
pub const SYN: u8 = 0x0;
pub const CONNECT: u8 = 0x1;
pub const DATA: u8 = 0x2;
pub const DISCONNECT: u8 = 0x3;
pub const PING: u8 = 0x4;
/// no idea what user is supposed to mean
pub const USER: u8 = 0x5;
}

View file

@ -0,0 +1,60 @@
use bytemuck::{Pod, Zeroable};
use std::{
fmt::{Debug, Formatter},
slice,
};
use v_byte_helpers::SwapEndian;
#[repr(transparent)]
#[derive(PartialEq, Eq, Ord, PartialOrd, Copy, Clone, Pod, Zeroable, SwapEndian, Hash, Default)]
pub struct VirtualPort(pub u8);
impl VirtualPort {
#[inline]
pub const fn get_stream_type(self) -> u8 {
(self.0 & 0xF0) >> 4
}
#[inline]
pub const fn get_port_number(self) -> u8 {
self.0 & 0x0F
}
#[inline]
pub fn stream_type(self, val: u8) -> Self {
let masked_val = val & 0x0F;
assert_eq!(masked_val, val);
Self((self.0 & 0x0F) | (masked_val << 4))
}
#[inline]
pub fn port_number(self, val: u8) -> Self {
let masked_val = val & 0x0F;
assert_eq!(masked_val, val);
Self((self.0 & 0xF0) | masked_val)
}
#[inline]
pub fn new(port: u8, stream_type: u8) -> Self {
Self(0).stream_type(stream_type).port_number(port)
}
#[inline(always)]
pub fn parse(data: &str) -> Option<Self> {
let (p1, p2) = data.split_once(':')?;
Some(Self::new(p1.parse().ok()?, p2.parse().ok()?))
}
}
impl Debug for VirtualPort {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let stream_type = self.get_stream_type();
let port_number = self.get_port_number();
write!(
f,
"VirtualPort{{ stream_type: {}, port_number: {} }}",
stream_type, port_number
)
}
}

60
rnex-core/src/reggie.rs Normal file
View file

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

View file

@ -2,7 +2,7 @@ use std::io;
use std::io::{Read, Seek, Write}; use std::io::{Read, Seek, Write};
use bytemuck::bytes_of; use bytemuck::bytes_of;
use log::error; use log::error;
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
use crate::rmc::response::{ErrorCode, RMCResponseResult}; use crate::rmc::response::{ErrorCode, RMCResponseResult};
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]

View file

@ -1,18 +1,20 @@
use crate::rmc::response::ErrorCode; use crate::rmc::structures::connection_data::{ConnectionData, ConnectionDataOld};
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}; use macros::{method_id, rmc_proto};
use rnex_core::rmc::response::ErrorCode;
use rnex_core::rmc::structures::any::Any;
use rnex_core::rmc::structures::qresult::QResult;
/// This is the representation for `Ticket Granting`(for details see the
/// This is the representation for `Ticket Granting`(for details see the
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
#[rmc_proto(10)] #[rmc_proto(10)]
pub trait Auth { pub trait Auth {
/// representation of the `Login` method(for details see the /// representation of the `Login` method(for details see the
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
#[method_id(1)] #[method_id(1)]
async fn login(&self, name: String) -> Result<(), ErrorCode>; async fn login(
&self,
name: String,
) -> Result<(QResult, u32, Vec<u8>, ConnectionDataOld, String), ErrorCode>;
/// representation of the `LoginEx` method(for details see the /// representation of the `LoginEx` method(for details see the
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))

View file

@ -0,0 +1,146 @@
use macros::{RmcSerialize, method_id, rmc_proto};
use rnex_core::{kerberos::KerberosDateTime, rmc::response::ErrorCode};
#[derive(RmcSerialize)]
#[rmc_struct(0)]
pub struct MiiV2 {
pub name: String,
pub unk: u8,
pub unk2: u8,
pub mii_data: Vec<u8>,
pub date_time: KerberosDateTime,
}
#[derive(RmcSerialize)]
#[rmc_struct(0)]
pub struct PrincipalBasicInfo {
pub pid: u32,
pub nnid: String,
pub mii: MiiV2,
pub unk: u8,
}
#[derive(RmcSerialize)]
#[rmc_struct(0)]
pub struct NNAInfo {
pub principal_basic_info: PrincipalBasicInfo,
pub unk: u8,
pub unk2: u8,
}
#[derive(RmcSerialize)]
#[rmc_struct(0)]
pub struct GameKey {
pub tid: u64,
pub version: u16,
}
#[derive(RmcSerialize)]
#[rmc_struct(0)]
pub struct NintendoPresenceV2 {
pub changed_flags: u32,
pub is_online: bool,
pub game_key: GameKey,
pub unk: u8,
pub message: String,
pub unk2: u32,
pub unk3: u8,
pub game_server_id: u32,
pub unk4: u32,
pub pid: u32,
pub gid: u32,
pub app_data: Vec<u8>,
pub unk5: u8,
pub unk6: u8,
pub unk7: u8,
}
#[derive(RmcSerialize)]
#[rmc_struct(0)]
pub struct PrincipalPreference {
pub show_online: bool,
pub show_playing_title: bool,
pub block_friend_request: bool,
}
#[derive(RmcSerialize)]
#[rmc_struct(0)]
pub struct Comment {
pub unk: u8,
pub message: String,
pub last_changed: KerberosDateTime,
}
#[derive(RmcSerialize)]
#[rmc_struct(0)]
pub struct FriendInfo {
pub nna_info: NNAInfo,
pub presence: NintendoPresenceV2,
pub comment: Comment,
pub became_friends: KerberosDateTime,
pub last_online: KerberosDateTime,
pub unk: u64,
}
#[derive(RmcSerialize)]
#[rmc_struct(0)]
pub struct FriendRequestMessage {
pub friend_request_id: u64,
pub is_recieved: u8,
pub unk: u8,
pub message: String,
pub unk2: u8,
pub unk3: String,
pub game_key: GameKey,
pub unk4: KerberosDateTime,
pub expires_on: KerberosDateTime,
}
#[derive(RmcSerialize)]
#[rmc_struct(0)]
pub struct FriendRequest {
pub basic_info: PrincipalBasicInfo,
pub request_message: FriendRequestMessage,
pub sent_on: KerberosDateTime,
}
#[derive(RmcSerialize)]
#[rmc_struct(0)]
pub struct BlacklistedPrincipal {
pub basic_info: PrincipalBasicInfo,
pub game_key: GameKey,
pub since: KerberosDateTime,
}
#[derive(RmcSerialize)]
#[rmc_struct(0)]
pub struct PersistentNotification {
pub unk1: u64,
pub unk2: u32,
pub unk3: u32,
pub unk4: u32,
pub unk5: String,
}
#[rmc_proto(102)]
pub trait Friends {
#[method_id(1)]
async fn update_and_get_all_information(
&self,
info: NNAInfo,
presence: NintendoPresenceV2,
date_time: KerberosDateTime,
) -> Result<
(
PrincipalPreference,
Comment,
Vec<FriendInfo>,
Vec<FriendRequest>,
Vec<FriendRequest>,
Vec<BlacklistedPrincipal>,
bool,
Vec<PersistentNotification>,
bool,
),
ErrorCode,
>;
}

View file

@ -1,6 +1,6 @@
use macros::{method_id, rmc_proto}; use macros::{method_id, rmc_proto};
use crate::prudp::station_url::StationUrl; use rnex_core::prudp::station_url::StationUrl;
use crate::rmc::response::ErrorCode; use rnex_core::rmc::response::ErrorCode;
#[rmc_proto(21)] #[rmc_proto(21)]
pub trait Matchmake{ pub trait Matchmake{
@ -8,10 +8,8 @@ pub trait Matchmake{
async fn unregister_gathering(&self, gid: u32) -> Result<bool, ErrorCode>; async fn unregister_gathering(&self, gid: u32) -> Result<bool, ErrorCode>;
#[method_id(41)] #[method_id(41)]
async fn get_session_urls(&self, gid: u32) -> Result<Vec<StationUrl>, ErrorCode>; async fn get_session_urls(&self, gid: u32) -> Result<Vec<StationUrl>, ErrorCode>;
#[method_id(42)] #[method_id(42)]
async fn update_session_host(&self, gid: u32, change_owner: bool) -> Result<(), ErrorCode>; async fn update_session_host(&self, gid: u32, change_owner: bool) -> Result<(), ErrorCode>;
#[method_id(44)] #[method_id(44)]
async fn migrate_gathering_ownership(&self, gid: u32, candidates: Vec<u32>, participants_only: bool) -> Result<(), ErrorCode>; async fn migrate_gathering_ownership(&self, gid: u32, candidates: Vec<u32>, participants_only: bool) -> Result<(), ErrorCode>;
} }

View file

@ -1,5 +1,5 @@
use macros::{method_id, rmc_proto}; use macros::{method_id, rmc_proto};
use crate::rmc::response::ErrorCode; use rnex_core::rmc::response::ErrorCode;
#[rmc_proto(50)] #[rmc_proto(50)]
pub trait MatchmakeExt{ pub trait MatchmakeExt{

View file

@ -1,6 +1,6 @@
use macros::{method_id, rmc_proto}; use macros::{method_id, rmc_proto};
use crate::rmc::response::ErrorCode; use rnex_core::rmc::response::ErrorCode;
use crate::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession}; use rnex_core::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession};
#[rmc_proto(109)] #[rmc_proto(109)]
pub trait MatchmakeExtension{ pub trait MatchmakeExtension{

View file

@ -1,20 +1,22 @@
#![allow(async_fn_in_trait)] #![allow(async_fn_in_trait)]
pub mod auth; pub mod auth;
pub mod secure; pub mod friends;
pub mod notifications;
pub mod matchmake; pub mod matchmake;
pub mod matchmake_ext;
pub mod matchmake_extension; pub mod matchmake_extension;
pub mod nat_traversal; pub mod nat_traversal;
pub mod matchmake_ext; pub mod notifications;
pub mod ranking; pub mod ranking;
pub mod secure;
use crate::util::{SendingBufferConnection, SplittableBufferConnection}; use crate::result::ResultExtension;
use crate::rmc::message::RMCMessage; use crate::rmc::message::RMCMessage;
use crate::rmc::protocols::RemoteCallError::ConnectionBroke; use crate::rmc::protocols::RemoteCallError::ConnectionBroke;
use crate::rmc::response::{ErrorCode, RMCResponse, RMCResponseResult}; use crate::rmc::response::{ErrorCode, RMCResponse, RMCResponseResult};
use crate::rmc::structures; use crate::rmc::structures;
use crate::rmc::structures::RmcSerialize; use crate::rmc::structures::RmcSerialize;
use crate::util::{SendingBufferConnection, SplittableBufferConnection};
use log::{error, info}; use log::{error, info};
use std::collections::HashMap; use std::collections::HashMap;
use std::future::Future; use std::future::Future;
@ -24,8 +26,7 @@ use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use thiserror::Error; use thiserror::Error;
use tokio::sync::{Mutex, Notify}; use tokio::sync::{Mutex, Notify};
use tokio::time::{sleep, sleep_until, Instant}; use tokio::time::{Instant, sleep, sleep_until};
use crate::result::ResultExtension;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum RemoteCallError { pub enum RemoteCallError {
@ -68,7 +69,7 @@ impl RmcConnection {
Ok(()) Ok(())
} }
pub async fn disconnect(&self){ pub async fn disconnect(&self) {
self.0.disconnect().await; self.0.disconnect().await;
} }
} }
@ -92,15 +93,11 @@ impl RmcResponseReceiver {
let mut locked = self.1.lock().await; let mut locked = self.1.lock().await;
if let Some(v) = locked.remove(&call_id) { if let Some(v) = locked.remove(&call_id) {
match v.response_result{ match v.response_result {
RMCResponseResult::Success { RMCResponseResult::Success { data, .. } => return Ok(data),
data, RMCResponseResult::Error { error_code, .. } => {
.. return Err(RemoteCallError::ServerError(error_code));
} => return Ok(data), }
RMCResponseResult::Error {
error_code,
..
} => return Err(RemoteCallError::ServerError(error_code))
} }
} }
@ -153,8 +150,9 @@ macro_rules! define_rmc_proto {
$($protocol:path),* $($protocol:path),*
}) => { }) => {
paste::paste!{ paste::paste!{
#[allow(unused_variables)]
pub trait [<Local $name>]: std::any::Any $( + [<Raw $protocol>] + $protocol)* { pub trait [<Local $name>]: std::any::Any $( + [<Raw $protocol>] + $protocol)* {
async fn rmc_call(&self, remote_response_connection: &rust_nex::util::SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec<u8>){ async fn rmc_call(&self, remote_response_connection: &rnex_core::util::SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec<u8>){
match protocol_id{ 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, [<Raw $protocol Info>]::PROTOCOL_ID => <Self as [<Raw $protocol>]>::rmc_call_proto(self, remote_response_connection, method_id, call_id, rest).await,
@ -164,19 +162,23 @@ macro_rules! define_rmc_proto {
} }
} }
pub struct [<Remote $name>](rust_nex::rmc::protocols::RmcConnection); pub struct [<Remote $name>](rnex_core::rmc::protocols::RmcConnection);
impl rust_nex::rmc::protocols::RemoteInstantiatable for [<Remote $name>]{ impl rnex_core::rmc::protocols::RmcPureRemoteObject for [<Remote $name>]{
fn new(conn: rust_nex::rmc::protocols::RmcConnection) -> Self{ fn new(conn: rnex_core::rmc::protocols::RmcConnection) -> Self{
Self(conn) Self(conn)
} }
}
impl rnex_core::rmc::protocols::RemoteDisconnectable for [<Remote $name>]{
async fn disconnect(&self){ async fn disconnect(&self){
self.0.disconnect().await; self.0.disconnect().await;
} }
} }
impl rust_nex::rmc::protocols::HasRmcConnection for [<Remote $name>]{ impl rnex_core::rmc::protocols::HasRmcConnection for [<Remote $name>]{
fn get_connection(&self) -> &rust_nex::rmc::protocols::RmcConnection{ fn get_connection(&self) -> &rnex_core::rmc::protocols::RmcConnection{
&self.0 &self.0
} }
} }
@ -202,14 +204,23 @@ impl RmcCallable for () {
} }
} }
pub trait RemoteInstantiatable{ pub trait RmcPureRemoteObject {
fn new(conn: RmcConnection) -> Self; fn new(conn: RmcConnection) -> Self;
}
pub trait RemoteDisconnectable {
async fn disconnect(&self); async fn disconnect(&self);
} }
pub struct OnlyRemote<T: RemoteInstantiatable>(T); pub struct OnlyRemote<T: RemoteDisconnectable>(T);
impl<T: RemoteInstantiatable> Deref for OnlyRemote<T>{ impl<T: RemoteDisconnectable + RmcPureRemoteObject> OnlyRemote<T> {
pub fn new(conn: RmcConnection) -> Self {
Self(T::new(conn))
}
}
impl<T: RemoteDisconnectable> Deref for OnlyRemote<T> {
type Target = T; type Target = T;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
@ -217,20 +228,23 @@ impl<T: RemoteInstantiatable> Deref for OnlyRemote<T>{
} }
} }
impl<T: RemoteInstantiatable> OnlyRemote<T>{ impl<T: RemoteDisconnectable> OnlyRemote<T> {
pub fn new(conn: RmcConnection) -> Self{
Self(T::new(conn))
}
pub async fn disconnect(&self) { pub async fn disconnect(&self) {
self.0.disconnect().await; self.0.disconnect().await;
} }
} }
impl<T: RemoteInstantiatable> RmcCallable for OnlyRemote<T>{ impl<T: RemoteDisconnectable> RmcCallable for OnlyRemote<T> {
fn rmc_call(&self, _responder: &SendingBufferConnection, _protocol_id: u16, _method_id: u32, _call_id: u32, _rest: Vec<u8>) -> impl Future<Output = ()> + Send { fn rmc_call(
&self,
_responder: &SendingBufferConnection,
_protocol_id: u16,
_method_id: u32,
_call_id: u32,
_rest: Vec<u8>,
) -> impl Future<Output = ()> + Send {
// maybe respond with not implemented or something // maybe respond with not implemented or something
async{} async {}
} }
} }
@ -242,23 +256,23 @@ async fn handle_incoming<T: RmcCallable + Send + Sync + 'static>(
) { ) {
let sending_conn = connection.duplicate_sender(); let sending_conn = connection.duplicate_sender();
while let Some(v) = connection.recv().await{ while let Some(v) = connection.recv().await {
let Some(proto_id) = v.get(4) else { let Some(proto_id) = v.get(4) else {
error!("received too small rmc message."); error!("received too small rmc message.");
error!("ending rmc gateway."); error!("ending rmc gateway.");
return return;
}; };
// protocol 0 is hardcoded to be the no protocol protocol aka keepalive protocol // protocol 0 is hardcoded to be the no protocol protocol aka keepalive protocol
if *proto_id == 0{ if *proto_id == 0 {
println!("got keepalive"); println!("got keepalive");
continue; continue;
} }
if (proto_id & 0x80) == 0{ if (proto_id & 0x80) == 0 {
let Some(response) = RMCResponse::new(&mut Cursor::new(v)).display_err_or_some() else { let Some(response) = RMCResponse::new(&mut Cursor::new(v)).display_err_or_some() else {
error!("ending rmc gateway."); error!("ending rmc gateway.");
return return;
}; };
info!("got rmc response"); info!("got rmc response");
@ -270,28 +284,34 @@ async fn handle_incoming<T: RmcCallable + Send + Sync + 'static>(
} else { } else {
let Some(message) = RMCMessage::new(&mut Cursor::new(v)).display_err_or_some() else { let Some(message) = RMCMessage::new(&mut Cursor::new(v)).display_err_or_some() else {
error!("ending rmc gateway."); error!("ending rmc gateway.");
return return;
}; };
let RMCMessage{ let RMCMessage {
protocol_id, protocol_id,
method_id, method_id,
call_id, call_id,
rest_of_data rest_of_data,
} = message; } = message;
info!("RMC REQUEST: Proto: {}; Method: {};", protocol_id, method_id); info!(
"RMC REQUEST: Proto: {}; Method: {}; cid: {}",
protocol_id, method_id, call_id
);
remote.rmc_call(&sending_conn, protocol_id, method_id, call_id, rest_of_data).await; remote
.rmc_call(&sending_conn, protocol_id, method_id, call_id, rest_of_data)
.await;
} }
} }
info!("rmc disconnected") info!("rmc disconnected")
} }
pub fn new_rmc_gateway_connection<T: RmcCallable + Sync + Send + 'static,F>(conn: SplittableBufferConnection, create_internal: F) -> Arc<T> pub fn new_rmc_gateway_connection<T: RmcCallable + Sync + Send + 'static, F>(
conn: SplittableBufferConnection,
create_internal: F,
) -> Arc<T>
where where
F: FnOnce(RmcConnection) -> Arc<T>, F: FnOnce(RmcConnection) -> Arc<T>,
{ {
@ -311,18 +331,12 @@ where
{ {
let exposed_object = exposed_object.clone(); let exposed_object = exposed_object.clone();
tokio::spawn(async move { tokio::spawn(async move {
handle_incoming( handle_incoming(conn, exposed_object, notify, incoming).await;
conn,
exposed_object,
notify,
incoming
).await;
}); });
tokio::spawn(async move { tokio::spawn(async move {
while sending_conn.is_alive(){ while sending_conn.is_alive() {
sending_conn.send([0,0,0,0,0].to_vec()).await; sending_conn.send([0, 0, 0, 0, 0].to_vec()).await;
sleep(Duration::from_secs(10)).await; sleep(Duration::from_secs(10)).await;
} }
}); });
@ -331,12 +345,20 @@ where
exposed_object exposed_object
} }
impl<T: RmcCallable> RmcCallable for Arc<T>{ impl<T: RmcCallable> RmcCallable for Arc<T> {
fn rmc_call(&self, responder: &SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec<u8>) -> impl Future<Output=()> + Send { fn rmc_call(
self.as_ref().rmc_call(responder, protocol_id, method_id, call_id, rest) &self,
responder: &SendingBufferConnection,
protocol_id: u16,
method_id: u32,
call_id: u32,
rest: Vec<u8>,
) -> impl Future<Output = ()> + Send {
self.as_ref()
.rmc_call(responder, protocol_id, method_id, call_id, rest)
} }
} }
define_rmc_proto! { define_rmc_proto! {
proto NoProto{} proto NoProto{}
} }

View file

@ -1,5 +1,5 @@
use macros::{method_id, rmc_proto}; use macros::{method_id, rmc_proto};
use crate::rmc::response::ErrorCode; use rnex_core::rmc::response::ErrorCode;
#[rmc_proto(3)] #[rmc_proto(3)]
pub trait NatTraversal{ pub trait NatTraversal{

View file

@ -0,0 +1,33 @@
use macros::{rmc_struct, rmc_proto, RmcSerialize};
#[derive(RmcSerialize, Debug, Default, Clone)]
struct ResultsRange{
offset: u32,
size: u32
}
#[derive(RmcSerialize, Debug, Default, Clone)]
#[rmc_struct(1)]
struct CompetitionRankingGetParam{
unk: u32,
range: ResultsRange,
festival_ids: Vec<u32>,
}
#[derive(RmcSerialize, Debug, Default, Clone)]
#[rmc_struct(0)]
struct CompetitionRankingScoreInfo{
fest_id: u32,
score_data: Vec<u32>,
unk: u32,
team_wins: Vec<u32>,
team_votes: Vec<u32>
}
#[rmc_proto(112)]
pub trait Ranking{
//#[method_id(16)]
//async fn competition_ranking_get_param(&self, param: CompetitionRankingGetParam) -> Result<Vec<CompetitionRankingScoreInfo>,ErrorCode>;
}

View file

@ -0,0 +1,23 @@
use macros::{method_id, rmc_proto};
use rnex_core::prudp::station_url::StationUrl;
use rnex_core::rmc::response::ErrorCode;
use rnex_core::rmc::structures::qresult::QResult;
use crate::rmc::structures::any::Any;
#[rmc_proto(11)]
pub trait Secure {
#[method_id(1)]
async fn register(
&self,
station_urls: Vec<StationUrl>,
) -> Result<(QResult, u32, StationUrl), ErrorCode>;
#[method_id(4)]
async fn register_ex(
&self,
station_urls: Vec<StationUrl>,
data: Any,
) -> Result<(QResult, u32, StationUrl), ErrorCode>;
#[method_id(7)]
async fn replace_url(&self, target: StationUrl, dest: StationUrl) -> Result<(), ErrorCode>;
}

View file

@ -7,8 +7,8 @@ use std::io::{Read, Seek, Write};
use std::mem::transmute; use std::mem::transmute;
use bytemuck::bytes_of; use bytemuck::bytes_of;
use log::error; use log::error;
use v_byte_macros::EnumTryInto; use v_byte_helpers::EnumTryInto;
use crate::endianness::{ReadExtensions, IS_BIG_ENDIAN}; use v_byte_helpers::{ReadExtensions, IS_BIG_ENDIAN};
use crate::rmc::response::ErrorCode::Core_Exception; use crate::rmc::response::ErrorCode::Core_Exception;
use crate::rmc::structures::qresult::ERROR_MASK; use crate::rmc::structures::qresult::ERROR_MASK;
use crate::util::SendingBufferConnection; use crate::util::SendingBufferConnection;
@ -471,7 +471,7 @@ mod test {
#[test] #[test]
fn test() { fn test() {
let mut data_orig = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 69, 4, 20]; let data_orig = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 69, 4, 20];
let mut data = data_orig; let mut data = data_orig;
let mut rc4: Rc4<U5> = let mut rc4: Rc4<U5> =

View file

@ -1,15 +1,15 @@
use rnex_core::rmc::structures::{Result, RmcSerialize};
use std::io::{Read, Write}; use std::io::{Read, Write};
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
use super::{Result, RmcSerialize};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Any{ pub struct Any {
pub name: String, pub name: String,
pub data: Vec<u8> pub data: Vec<u8>,
} }
impl RmcSerialize for Any{ impl RmcSerialize for Any {
fn serialize(&self, writer: &mut dyn Write) -> Result<()> { fn serialize(&self, writer: &mut impl Write) -> Result<()> {
self.name.serialize(writer)?; self.name.serialize(writer)?;
let u32_len = self.data.len() as u32; let u32_len = self.data.len() as u32;
@ -21,7 +21,7 @@ impl RmcSerialize for Any{
Ok(()) Ok(())
} }
fn deserialize(mut reader: &mut dyn Read) -> Result<Self> { fn deserialize(reader: &mut impl Read) -> Result<Self> {
let name = String::deserialize(reader)?; let name = String::deserialize(reader)?;
// also length ? // also length ?
@ -32,11 +32,6 @@ impl RmcSerialize for Any{
reader.read_exact(&mut data)?; reader.read_exact(&mut data)?;
Ok( Ok(Any { name, data })
Any{
name,
data
}
)
} }
} }

View file

@ -0,0 +1,36 @@
use crate::rmc::structures::Result;
use crate::rmc::structures::RmcSerialize;
use std::io::{Read, Write};
impl<'a> RmcSerialize for &'a [u8] {
fn serialize(&self, writer: &mut impl Write) -> Result<()> {
let u32_size = self.len() as u32;
writer.write(bytemuck::bytes_of(&u32_size))?;
writer.write(self)?;
Ok(())
}
/// DO NOT USE (also maybe split off the serialize and deserialize functions at some point)
fn deserialize(_reader: &mut impl Read) -> Result<Self> {
panic!("cannot deserialize to a u8 slice reference (use this ONLY for writing)")
}
fn serialize_write_size(&self) -> Result<u32> {
Ok(4 + self.len() as u32)
}
}
impl RmcSerialize for Box<[u8]> {
fn serialize(&self, writer: &mut impl Write) -> Result<()> {
(&self[..]).serialize(writer)
}
fn deserialize(reader: &mut impl Read) -> Result<Self> {
Vec::deserialize(reader).map(|v| v.into_boxed_slice())
}
fn serialize_write_size(&self) -> Result<u32> {
(&self[..]).serialize_write_size()
}
}

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