V0 #2
140 changed files with 6157 additions and 5913 deletions
17
.ci-scripts/make-edition.sh
Executable file
17
.ci-scripts/make-edition.sh
Executable 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
3
.devcontainer.json
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"image": "ci.virintox.com/spfn/rust-nex/dev-container:latest"
|
||||
}
|
||||
|
|
@ -1,2 +1,10 @@
|
|||
.env
|
||||
target
|
||||
target
|
||||
.dockerignore
|
||||
Dockerfile
|
||||
CODE_OF_CONDUCT.md
|
||||
CONTRIBUTING.md
|
||||
README.md
|
||||
.gitignore
|
||||
LICENSE
|
||||
.devcontainer.json
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -2,4 +2,5 @@ target
|
|||
.idea
|
||||
.env
|
||||
log
|
||||
reports
|
||||
reports
|
||||
.zed
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
IMAGE_NAME: "ci.perditum.com/perditum/rnex-splatoon"
|
||||
DOCKER_TLS_CERTDIR: "/certs"
|
||||
IMAGE_TAG: "${CI_COMMIT_REF_SLUG}"
|
||||
|
||||
before_script:
|
||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" ci.perditum.com
|
||||
|
||||
stages:
|
||||
- build-and-push
|
||||
- build_and_test
|
||||
|
||||
build-and-push:
|
||||
stage: build-and-push
|
||||
script:
|
||||
- docker build -t "$IMAGE_NAME:$IMAGE_TAG" .
|
||||
- docker tag "$IMAGE_NAME:$IMAGE_TAG" "$IMAGE_NAME:latest"
|
||||
- docker push "$IMAGE_NAME:$IMAGE_TAG"
|
||||
- docker push "$IMAGE_NAME:latest"
|
||||
splatoon:
|
||||
stage: build_and_test
|
||||
script: ./.ci-scripts/make-edition.sh splatoon
|
||||
|
||||
friends:
|
||||
stage: build_and_test
|
||||
script: ./.ci-scripts/make-edition.sh friends
|
||||
|
|
|
|||
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -1,3 +0,0 @@
|
|||
[submodule "grpc-protobufs"]
|
||||
path = grpc-protobufs
|
||||
url = https://github.com/PretendoNetwork/grpc-protobufs.git
|
||||
2831
Cargo.lock
generated
2831
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
99
Cargo.toml
99
Cargo.toml
|
|
@ -1,91 +1,8 @@
|
|||
[package]
|
||||
name = "rust-nex"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.prod]
|
||||
inherits = "release"
|
||||
overflow-checks = false
|
||||
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"
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = [
|
||||
"macros",
|
||||
"rnex-core",
|
||||
"prudpv1",
|
||||
"prudpv0"
|
||||
, "proxy", "proxy-common"]
|
||||
|
|
|
|||
40
Dockerfile
40
Dockerfile
|
|
@ -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
|
||||
|
||||
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 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 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 ./test-edition.sh
|
||||
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
|
||||
ENTRYPOINT ["/splatoon-server-rust"]
|
||||
FROM scratch AS proxy-insecure
|
||||
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
|
||||
|
|
|
|||
15
README.md
15
README.md
|
|
@ -4,4 +4,17 @@
|
|||
- 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/)
|
||||
- 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
13
build-edition.sh
Executable 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
10
buildscripts/common.sh
Executable 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
17
editions.yaml
Normal 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
61
flake.lock
generated
Normal 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
28
flake.nix
Normal 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
113
macros/Cargo.lock
generated
|
|
@ -2,15 +2,55 @@
|
|||
# It is not intended for manual editing.
|
||||
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]]
|
||||
name = "macros"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rand",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.93"
|
||||
|
|
@ -29,6 +69,41 @@ dependencies = [
|
|||
"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]]
|
||||
name = "syn"
|
||||
version = "2.0.98"
|
||||
|
|
@ -45,3 +120,41 @@ name = "unicode-ident"
|
|||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ edition = "2018"
|
|||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
quote = "1.0.38"
|
||||
proc-macro2 = "1.0.93"
|
||||
syn = { version = "2.0.98", features = ["full"] }
|
||||
rand = "0.9.0"
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ impl Parse for ProtoInputParams {
|
|||
fn gen_serialize_data_struct(
|
||||
s: DataStruct,
|
||||
struct_attr: Option<&Attribute>,
|
||||
) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
|
||||
) -> (proc_macro2::TokenStream, proc_macro2::TokenStream, proc_macro2::TokenStream) {
|
||||
let serialize_base_content = {
|
||||
let mut serialize_content = quote! {};
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ fn gen_serialize_data_struct(
|
|||
let ident = f.ident.as_ref().unwrap();
|
||||
|
||||
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
|
||||
|
||||
let serialize_base_content = if let Some(attr) = struct_attr {
|
||||
|
|
@ -143,9 +165,18 @@ fn gen_serialize_data_struct(
|
|||
|
||||
quote! {
|
||||
#pre_inner
|
||||
rust_nex::rmc::structures::rmc_struct::write_struct(writer, #version, |mut writer|{
|
||||
#serialize_base_content
|
||||
})?;
|
||||
rnex_core::rmc::structures::rmc_struct::write_struct(
|
||||
writer,
|
||||
#version,
|
||||
rnex_core::rmc::structures::helpers::len_of_write(
|
||||
|writer|{
|
||||
#serialize_base_content
|
||||
}
|
||||
),
|
||||
|writer|{
|
||||
#serialize_base_content
|
||||
}
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -176,7 +207,7 @@ fn gen_serialize_data_struct(
|
|||
|
||||
quote! {
|
||||
#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
|
||||
})?)
|
||||
}
|
||||
|
|
@ -184,7 +215,13 @@ fn gen_serialize_data_struct(
|
|||
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))]
|
||||
|
|
@ -210,7 +247,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
|
|||
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::Enum(e) => {
|
||||
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_se = quote! {};
|
||||
//let mut inner_match_len = quote!{};
|
||||
|
||||
for variant in e.variants {
|
||||
let Some((_, val)) = variant.discriminant else {
|
||||
|
|
@ -235,7 +273,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
|
|||
let name = &field.ident;
|
||||
|
||||
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;
|
||||
|
||||
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! {
|
||||
<#ty as rust_nex::rmc::structures::RmcSerialize>::serialize(&#val, writer)?;
|
||||
<#ty as rnex_core::rmc::structures::RmcSerialize>::serialize(&#val, writer)?;
|
||||
};
|
||||
|
||||
match &variant.fields {
|
||||
|
|
@ -270,7 +308,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
|
|||
let name = &field.ident;
|
||||
|
||||
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());
|
||||
|
||||
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 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
|
||||
v => return Err(rust_nex::rmc::structures::Error::UnexpectedValue(v as _))
|
||||
v => return Err(rnex_core::rmc::structures::Error::UnexpectedValue(v as _))
|
||||
};
|
||||
|
||||
Ok(val)
|
||||
};
|
||||
|
||||
(serialize_base_content, deserialize_base_content)
|
||||
(serialize_base_content, deserialize_base_content, quote!{})
|
||||
}
|
||||
Data::Union(_) => {
|
||||
unimplemented!()
|
||||
|
|
@ -364,16 +402,17 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
|
|||
let ident = derive_input.ident;
|
||||
|
||||
let tokens = quote! {
|
||||
impl rust_nex::rmc::structures::RmcSerialize for #ident{
|
||||
fn serialize(&self, writer: &mut dyn ::std::io::Write) -> rust_nex::rmc::structures::Result<()>{
|
||||
impl rnex_core::rmc::structures::RmcSerialize for #ident{
|
||||
#[inline(always)]
|
||||
fn serialize(&self, writer: &mut impl ::std::io::Write) -> rnex_core::rmc::structures::Result<()>{
|
||||
#serialize_base_content
|
||||
|
||||
|
||||
}
|
||||
|
||||
fn deserialize(reader: &mut dyn ::std::io::Read) -> rust_nex::rmc::structures::Result<Self>{
|
||||
#[inline(always)]
|
||||
fn deserialize(reader: &mut impl ::std::io::Read) -> rnex_core::rmc::structures::Result<Self>{
|
||||
#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{
|
||||
async fn rmc_call(&self, remote_response_connection: &rust_nex::util::SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec<u8>){
|
||||
impl rnex_core::rmc::protocols::RmcCallable for #struct_name{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ impl RmcProtocolData{
|
|||
// boilerplate tokens which all raw traits need
|
||||
quote!{
|
||||
#[doc(hidden)]
|
||||
#[allow(unused_must_use)]
|
||||
pub trait #raw_name: #name
|
||||
}.to_tokens(tokens);
|
||||
|
||||
|
|
@ -54,6 +55,7 @@ impl RmcProtocolData{
|
|||
|
||||
let raw_name = Ident::new(&format!("raw_{}", name), name.span());
|
||||
quote!{
|
||||
#[inline(always)]
|
||||
async fn #raw_name
|
||||
}.to_tokens(tokens);
|
||||
|
||||
|
|
@ -73,7 +75,7 @@ impl RmcProtocolData{
|
|||
for (param_name, param_type) in parameters{
|
||||
quote!{
|
||||
let Ok(#param_name) =
|
||||
<#param_type as rust_nex::rmc::structures::RmcSerialize>::deserialize(
|
||||
<#param_type as rnex_core::rmc::structures::RmcSerialize>::deserialize(
|
||||
&mut cursor
|
||||
) else
|
||||
}.to_tokens(tokens);
|
||||
|
|
@ -84,7 +86,7 @@ impl RmcProtocolData{
|
|||
quote! {
|
||||
{
|
||||
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)
|
||||
} else {
|
||||
|
|
@ -116,7 +118,7 @@ impl RmcProtocolData{
|
|||
quote!{
|
||||
let retval = retval?;
|
||||
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)
|
||||
}.to_tokens(tokens);
|
||||
}
|
||||
|
|
@ -124,9 +126,10 @@ impl RmcProtocolData{
|
|||
}
|
||||
|
||||
quote!{
|
||||
#[inline(always)]
|
||||
async fn rmc_call_proto(
|
||||
&self,
|
||||
remote_response_connection: &rust_nex::util::SendingBufferConnection,
|
||||
remote_response_connection: &rnex_core::util::SendingBufferConnection,
|
||||
method_id: u32,
|
||||
call_id: u32,
|
||||
data: Vec<u8>,
|
||||
|
|
@ -165,7 +168,7 @@ impl RmcProtocolData{
|
|||
}.to_tokens(tokens);
|
||||
if self.has_returns {
|
||||
quote! {
|
||||
Err(rust_nex::rmc::response::ErrorCode::Core_NotImplemented)
|
||||
Err(rnex_core::rmc::response::ErrorCode::Core_NotImplemented)
|
||||
}.to_tokens(tokens);
|
||||
}
|
||||
});
|
||||
|
|
@ -176,7 +179,7 @@ impl RmcProtocolData{
|
|||
|
||||
if *has_returns{
|
||||
quote!{
|
||||
rust_nex::rmc::response::send_result(
|
||||
rnex_core::rmc::response::send_result(
|
||||
remote_response_connection,
|
||||
ret,
|
||||
#id,
|
||||
|
|
@ -209,7 +212,8 @@ impl RmcProtocolData{
|
|||
// boilerplate tokens which all raw traits need
|
||||
quote!{
|
||||
#[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);
|
||||
|
||||
// generate the body of the raw protocol trait
|
||||
|
|
@ -247,12 +251,12 @@ impl RmcProtocolData{
|
|||
|
||||
for (param_name, param_type) in parameters{
|
||||
quote!{
|
||||
rust_nex::result::ResultExtension::display_err_or_some(
|
||||
<#param_type as rust_nex::rmc::structures::RmcSerialize>::serialize(
|
||||
rnex_core::result::ResultExtension::display_err_or_some(
|
||||
<#param_type as rnex_core::rmc::structures::RmcSerialize>::serialize(
|
||||
&#param_name,
|
||||
&mut cursor
|
||||
)
|
||||
).ok_or(rust_nex::rmc::response::ErrorCode::Core_InvalidArgument)
|
||||
).ok_or(rnex_core::rmc::response::ErrorCode::Core_InvalidArgument)
|
||||
}.to_tokens(tokens);
|
||||
if self.has_returns {
|
||||
quote! {
|
||||
|
|
@ -268,25 +272,25 @@ impl RmcProtocolData{
|
|||
quote!{
|
||||
let call_id = rand::random();
|
||||
|
||||
let message = rust_nex::rmc::message::RMCMessage{
|
||||
let message = rnex_core::rmc::message::RMCMessage{
|
||||
call_id,
|
||||
method_id: #method_id,
|
||||
protocol_id: #proto_id,
|
||||
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);
|
||||
|
||||
if *has_returns{
|
||||
quote!{
|
||||
rust_nex::result::ResultExtension::display_err_or_some(
|
||||
rnex_core::result::ResultExtension::display_err_or_some(
|
||||
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);
|
||||
} else {
|
||||
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
|
||||
);
|
||||
}.to_tokens(tokens);
|
||||
|
|
@ -325,10 +329,5 @@ impl ToTokens for RmcProtocolData{
|
|||
self.generate_raw_trait(tokens);
|
||||
self.generate_raw_info(tokens);
|
||||
self.generate_raw_remote_trait(tokens);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
BIN
out/backend_server_secure
Executable file
BIN
out/backend_server_secure
Executable file
Binary file not shown.
BIN
perf.data
Normal file
BIN
perf.data
Normal file
Binary file not shown.
10
proxy-common/Cargo.toml
Normal file
10
proxy-common/Cargo.toml
Normal 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
215
proxy-common/src/lib.rs
Normal 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(¶m.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
28
proxy/Cargo.toml
Normal 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
15
proxy/src/insecure.rs
Normal 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(¶m, edge_node_dc_callback).await;
|
||||
|
||||
proxy::start_insecure(param).await;
|
||||
}
|
||||
19
proxy/src/lib.rs
Normal file
19
proxy/src/lib.rs
Normal 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
14
proxy/src/secure.rs
Normal 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(¶m, edge_node_dc_callback).await;
|
||||
proxy::start_secure(param).await;
|
||||
}
|
||||
20
prudpv0/Cargo.toml
Normal file
20
prudpv0/Cargo.toml
Normal 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"]
|
||||
54
prudpv0/src/crypto/common_crypto.rs
Normal file
54
prudpv0/src/crypto/common_crypto.rs
Normal 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
|
||||
}
|
||||
5
prudpv0/src/crypto/friends_common.rs
Normal file
5
prudpv0/src/crypto/friends_common.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
use hmac::Hmac;
|
||||
use md5::Md5;
|
||||
|
||||
pub const ACCESS_KEY: &str = "ridfebb9";
|
||||
pub type HmacMd5 = Hmac<Md5>;
|
||||
78
prudpv0/src/crypto/friends_insecure.rs
Normal file
78
prudpv0/src/crypto/friends_insecure.rs
Normal 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![],
|
||||
))
|
||||
}
|
||||
}
|
||||
96
prudpv0/src/crypto/friends_secure.rs
Normal file
96
prudpv0/src/crypto/friends_secure.rs
Normal 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,
|
||||
))
|
||||
}
|
||||
}
|
||||
9
prudpv0/src/crypto/insecure.rs
Normal file
9
prudpv0/src/crypto/insecure.rs
Normal 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
38
prudpv0/src/crypto/mod.rs
Normal 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::*;
|
||||
}
|
||||
}
|
||||
9
prudpv0/src/crypto/secure.rs
Normal file
9
prudpv0/src/crypto/secure.rs
Normal 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
65
prudpv0/src/lib.rs
Normal 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
405
prudpv0/src/packet.rs
Normal 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
505
prudpv0/src/server.rs
Normal 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
23
prudpv1/Cargo.toml
Normal 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 = []
|
||||
17
prudpv1/src/executables/common.rs
Normal file
17
prudpv1/src/executables/common.rs
Normal 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")
|
||||
});
|
||||
3
prudpv1/src/executables/mod.rs
Normal file
3
prudpv1/src/executables/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pub mod common;
|
||||
pub mod proxy_insecure;
|
||||
pub mod proxy_secure;
|
||||
|
|
@ -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::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use futures::future::Remote;
|
||||
use log::{error, warn};
|
||||
use macros::rmc_struct;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::task;
|
||||
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]
|
||||
async fn main() {
|
||||
setup();
|
||||
|
||||
let conn = tokio::net::TcpStream::connect(&*SECURE_EDGE_NODE_HOLDER).await.unwrap();
|
||||
pub async fn start() {
|
||||
/*let conn = tokio::net::TcpStream::connect(&*EDGE_NODE_HOLDER)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let conn: SplittableBufferConnection = conn.into();
|
||||
|
||||
conn.send(Register(SocketAddrV4::new(*OWN_IP_PUBLIC, *SERVER_PORT)).to_data()).await;
|
||||
|
||||
let conn = new_rmc_gateway_connection(conn, |r| Arc::new(OnlyRemote::<RemoteEdgeNodeHolder>::new(r)));
|
||||
|
||||
conn.send(
|
||||
Register(SocketAddrV4::new(*OWN_IP_PUBLIC, *SERVER_PORT))
|
||||
.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))
|
||||
.await
|
||||
.expect("unable to start router");
|
||||
|
||||
let mut socket_secure = router_secure
|
||||
.add_socket(VirtualPort::new(1, 10), Secure(
|
||||
"6f599f81",
|
||||
SECURE_SERVER_ACCOUNT.clone()
|
||||
))
|
||||
.add_socket(VirtualPort::new(1, 10), Unsecure("6f599f81"))
|
||||
.await
|
||||
.expect("unable to add socket");
|
||||
|
||||
|
|
@ -60,8 +56,7 @@ async fn main() {
|
|||
};
|
||||
|
||||
task::spawn(async move {
|
||||
let mut stream
|
||||
= match TcpStream::connect(*FORWARD_DESTINATION).await {
|
||||
let mut stream = match TcpStream::connect(*FORWARD_DESTINATION).await {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!("unable to connect: {}", e);
|
||||
|
|
@ -69,26 +64,31 @@ async fn main() {
|
|||
}
|
||||
};
|
||||
|
||||
if let Err(e) = stream.send_buffer(&ConnectionInitData{
|
||||
prudpsock_addr: conn.socket_addr,
|
||||
pid: conn.user_id
|
||||
}.to_data()).await{
|
||||
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 {
|
||||
break;
|
||||
return;
|
||||
};
|
||||
|
||||
if let Err(e) = stream.send_buffer(&data[..]).await{
|
||||
error!("error sending data to backend: {}", e);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
},
|
||||
data = stream.read_buffer() => {
|
||||
|
|
@ -96,10 +96,10 @@ async fn main() {
|
|||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
error!("error reveiving data from backend: {}", e);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if conn.send(data).await == None{
|
||||
return;
|
||||
}
|
||||
|
|
@ -111,4 +111,4 @@ async fn main() {
|
|||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
102
prudpv1/src/executables/proxy_secure.rs
Normal file
102
prudpv1/src/executables/proxy_secure.rs
Normal 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
14
prudpv1/src/lib.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
/*
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
pub trait AuthModule{
|
||||
fn get_auth_key(addr: Ipv4Addr) -> [u8; 32];
|
||||
}
|
||||
}*/
|
||||
/*
|
||||
struct AuthServerAuthModule;
|
||||
|
||||
|
|
@ -2,7 +2,5 @@ pub mod packet;
|
|||
pub mod router;
|
||||
pub mod socket;
|
||||
mod auth_module;
|
||||
pub mod sockaddr;
|
||||
pub mod station_url;
|
||||
pub mod secure;
|
||||
pub mod unsecure;
|
||||
|
|
@ -3,20 +3,24 @@
|
|||
// force the compiler to shut up here
|
||||
#![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::io;
|
||||
use std::io::{Cursor, Read, Seek, Write};
|
||||
use std::net::SocketAddrV4;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use hmac::{Hmac, Mac};
|
||||
use log::{error, warn};
|
||||
use md5::{Md5, Digest};
|
||||
use thiserror::Error;
|
||||
use v_byte_macros::{SwapEndian};
|
||||
use crate::endianness::{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;
|
||||
use v_byte_helpers::SwapEndian;
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
|
||||
type Md5Hmac = Hmac<Md5>;
|
||||
|
||||
|
|
@ -31,117 +35,11 @@ pub enum Error {
|
|||
#[error("invalid option id {0}")]
|
||||
InvalidOptionId(u8),
|
||||
#[error("option size {size} doesnt match expected option for given option id {id}")]
|
||||
InvalidOptionSize {
|
||||
id: u8,
|
||||
size: u8,
|
||||
},
|
||||
InvalidOptionSize { id: u8, size: u8 },
|
||||
}
|
||||
|
||||
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)]
|
||||
#[derive(Debug, Copy, Clone, Pod, Zeroable, SwapEndian, Eq, PartialEq)]
|
||||
pub struct PRUDPV1Header {
|
||||
|
|
@ -159,7 +57,7 @@ pub struct PRUDPV1Header {
|
|||
|
||||
impl Default for PRUDPV1Header {
|
||||
fn default() -> Self {
|
||||
Self{
|
||||
Self {
|
||||
magic: [0xEA, 0xD0],
|
||||
version: 1,
|
||||
session_id: 0,
|
||||
|
|
@ -169,32 +67,30 @@ impl Default for PRUDPV1Header {
|
|||
destination_port: VirtualPort(0),
|
||||
types_and_flags: TypesFlags(0),
|
||||
packet_specific_size: 0,
|
||||
substream_id: 0
|
||||
substream_id: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum PacketOption{
|
||||
pub enum PacketOption {
|
||||
SupportedFunctions(u32),
|
||||
ConnectionSignature([u8; 16]),
|
||||
FragmentId(u8),
|
||||
InitialSequenceId(u16),
|
||||
MaximumSubstreamId(u8)
|
||||
MaximumSubstreamId(u8),
|
||||
}
|
||||
|
||||
impl PacketOption{
|
||||
fn from(option_id: OptionId, option_data: &[u8]) -> io::Result<Self>{
|
||||
|
||||
impl PacketOption {
|
||||
fn from(option_id: OptionId, option_data: &[u8]) -> io::Result<Self> {
|
||||
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)?),
|
||||
1 => ConnectionSignature(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)?),
|
||||
4 => MaximumSubstreamId(data_cursor.read_struct(IS_BIG_ENDIAN)?),
|
||||
_ => unreachable!()
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Ok(val)
|
||||
|
|
@ -255,7 +151,7 @@ impl OptionId {
|
|||
// Invariant is upheld because we only create the object if it doesn't violate the invariant
|
||||
match 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,
|
||||
3 => 2,
|
||||
4 => 1,
|
||||
_ => unreachable!()
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -281,8 +177,7 @@ impl PRUDPV1Packet {
|
|||
pub fn new(reader: &mut (impl Read + Seek)) -> Result<Self> {
|
||||
let header: PRUDPV1Header = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||
|
||||
if header.magic[0] != 0xEA ||
|
||||
header.magic[1] != 0xD0 {
|
||||
if header.magic[0] != 0xEA || header.magic[1] != 0xD0 {
|
||||
return Err(Error::InvalidMagic(u16::from_be_bytes(header.magic)));
|
||||
}
|
||||
|
||||
|
|
@ -290,31 +185,31 @@ impl PRUDPV1Packet {
|
|||
return Err(Error::InvalidVersion(header.version));
|
||||
}
|
||||
|
||||
|
||||
let packet_signature: [u8; 16] = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||
//let packet_signature: [u8; 16] = [0; 16];
|
||||
|
||||
assert_eq!(reader.stream_position().ok(), Some(14 + 16));
|
||||
|
||||
|
||||
|
||||
let mut packet_specific_buffer = vec![0u8; header.packet_specific_size as usize];
|
||||
|
||||
reader.read_exact(&mut packet_specific_buffer)?;
|
||||
|
||||
|
||||
//no clue whats up with options but they are broken
|
||||
let mut packet_specific_data_cursor = Cursor::new(&packet_specific_buffer);
|
||||
|
||||
let mut options = Vec::new();
|
||||
|
||||
loop {
|
||||
let Ok(option_id): io::Result<u8> = packet_specific_data_cursor.read_struct(IS_BIG_ENDIAN) else {
|
||||
break
|
||||
let Ok(option_id): io::Result<u8> =
|
||||
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 {
|
||||
break
|
||||
let Ok(value_size): io::Result<u8> =
|
||||
packet_specific_data_cursor.read_struct(IS_BIG_ENDIAN)
|
||||
else {
|
||||
break;
|
||||
};
|
||||
|
||||
if value_size == 0 {
|
||||
|
|
@ -334,20 +229,21 @@ impl PRUDPV1Packet {
|
|||
}
|
||||
|
||||
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");
|
||||
break;
|
||||
}
|
||||
|
||||
options.push(PacketOption::from(option_id, &option_data)?);
|
||||
}
|
||||
|
||||
|
||||
let mut payload = vec![0u8; header.payload_size as usize];
|
||||
|
||||
reader.read_exact(&mut payload)?;
|
||||
|
||||
|
||||
|
||||
Ok(Self {
|
||||
header,
|
||||
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 mut flags = self.header.types_and_flags.flags(0);
|
||||
|
||||
flags.set_flag(ACK);
|
||||
|
||||
let options = self.options
|
||||
let options = self
|
||||
.options
|
||||
.iter()
|
||||
.filter(|o| matches!(o, FragmentId(_)))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
|
||||
|
||||
Self{
|
||||
Self {
|
||||
header: PRUDPV1Header {
|
||||
types_and_flags: flags,
|
||||
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();
|
||||
|
||||
for option in &self.options{
|
||||
option.write_to_stream(&mut vec).expect("vec should always automatically be able to extend");
|
||||
for option in &self.options {
|
||||
option
|
||||
.write_to_stream(&mut vec)
|
||||
.expect("vec should always automatically be able to extend");
|
||||
}
|
||||
|
||||
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_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();
|
||||
|
|
@ -417,32 +319,46 @@ impl PRUDPV1Packet {
|
|||
|
||||
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 {
|
||||
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 {
|
||||
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]>){
|
||||
self.packet_signature = self.calculate_signature_value(access_key, session_key, connection_signature);
|
||||
pub fn calculate_and_assign_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.payload_size = self.payload.len() as u16;
|
||||
}
|
||||
|
||||
pub fn base_response_packet(&self) -> Self {
|
||||
pub fn base_response_packet(&self) -> Self {
|
||||
Self {
|
||||
header: PRUDPV1Header {
|
||||
magic: [0xEA, 0xD0],
|
||||
|
|
@ -455,19 +371,18 @@ impl PRUDPV1Packet {
|
|||
sequence_id: 0,
|
||||
session_id: 0,
|
||||
substream_id: 0,
|
||||
|
||||
},
|
||||
packet_signature: [0; 16],
|
||||
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(&self.packet_signature)?;
|
||||
|
||||
for option in &self.options{
|
||||
for option in &self.options {
|
||||
option.write_to_stream(writer)?;
|
||||
}
|
||||
|
||||
|
|
@ -479,19 +394,24 @@ impl PRUDPV1Packet {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::prudp::packet::flags::{NEED_ACK, RELIABLE};
|
||||
use crate::prudp::packet::types::DATA;
|
||||
use super::{OptionId, PacketOption, PRUDPV1Header, TypesFlags, VirtualPort};
|
||||
use super::{OptionId, PRUDPV1Header, PacketOption, TypesFlags};
|
||||
use rnex_core::prudp::{
|
||||
types_flags::{
|
||||
flags::{NEED_ACK, RELIABLE},
|
||||
types::DATA,
|
||||
},
|
||||
virtual_port::VirtualPort,
|
||||
};
|
||||
#[test]
|
||||
fn size_test() {
|
||||
assert_eq!(size_of::<PRUDPV1Header>(), 14);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_options(){
|
||||
let packet_types = [0,1,2,3,4];
|
||||
fn test_options() {
|
||||
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 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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn header_read(){
|
||||
fn header_read() {
|
||||
let header = PRUDPV1Header {
|
||||
version: 0,
|
||||
destination_port: VirtualPort(0),
|
||||
|
|
@ -520,8 +438,8 @@ mod test {
|
|||
packet_specific_size: 0,
|
||||
payload_size: 0,
|
||||
sequence_id: 0,
|
||||
magic: [0xEA,0xD0],
|
||||
source_port: VirtualPort(0)
|
||||
magic: [0xEA, 0xD0],
|
||||
source_port: VirtualPort(0),
|
||||
};
|
||||
|
||||
let bytes = bytemuck::bytes_of(&header);
|
||||
|
|
@ -532,11 +450,11 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_types_flags(){
|
||||
fn test_types_flags() {
|
||||
let types = TypesFlags::default().types(DATA).flags(NEED_ACK | RELIABLE);
|
||||
|
||||
assert_ne!((types.0 >> 4) & NEED_ACK, 0);
|
||||
assert_ne!((types.0 >> 4) & RELIABLE, 0);
|
||||
assert_ne!((types.0 & 0xFF) as u8 & DATA, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
prudpv1/src/prudp/packet/v1/sanity_checks.rs
Normal file
12
prudpv1/src/prudp/packet/v1/sanity_checks.rs
Normal 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");
|
||||
|
|
@ -1,33 +1,27 @@
|
|||
use std::{env, io};
|
||||
use std::io;
|
||||
use std::io::Cursor;
|
||||
use std::marker::PhantomData;
|
||||
use tokio::net::UdpSocket;
|
||||
use std::net::{SocketAddr, SocketAddrV4};
|
||||
use std::net::SocketAddr::V4;
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::sync::atomic::{AtomicBool};
|
||||
use std::time::Duration;
|
||||
use tokio::task::JoinHandle;
|
||||
use once_cell::sync::Lazy;
|
||||
use log::{error, info};
|
||||
use thiserror::Error;
|
||||
use tokio::select;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::time::sleep;
|
||||
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;
|
||||
|
||||
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 {
|
||||
endpoints: RwLock<[Option<Arc<dyn AnyInternalSocket>>; 16]>,
|
||||
running: AtomicBool,
|
||||
//running: AtomicBool,
|
||||
socket: Arc<UdpSocket>,
|
||||
_no_outside_construction: PhantomData<()>
|
||||
}
|
||||
|
|
@ -113,7 +107,7 @@ impl Router {
|
|||
|
||||
let own_impl = Router {
|
||||
endpoints: Default::default(),
|
||||
running: AtomicBool::new(true),
|
||||
// running: AtomicBool::new(true),
|
||||
socket: socket.clone(),
|
||||
_no_outside_construction: Default::default()
|
||||
};
|
||||
|
|
@ -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 log::error;
|
||||
use rc4::cipher::StreamCipherCoreWrapper;
|
||||
use rc4::{KeyInit, Rc4, Rc4Core, StreamCipher};
|
||||
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 crate::endianness::{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))
|
||||
}
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
|
||||
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);
|
||||
|
||||
vec.push(EncryptionPair{
|
||||
vec.push(EncryptionPair {
|
||||
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 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);
|
||||
}
|
||||
|
||||
vec.push(EncryptionPair{
|
||||
vec.push(EncryptionPair {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
pub struct Secure(pub &'static str, pub Account);
|
||||
|
||||
|
||||
pub struct SecureInstance {
|
||||
access_key: &'static str,
|
||||
session_key: [u8; 32],
|
||||
|
|
@ -155,18 +97,17 @@ impl CryptoHandler for Secure {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
impl CryptoHandlerConnectionInstance for SecureInstance {
|
||||
type Encryption = Rc4<U5>;
|
||||
|
||||
fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]) {
|
||||
if let Some(crypt_pair) = self.streams.get_mut(substream as usize){
|
||||
if let Some(crypt_pair) = self.streams.get_mut(substream as usize) {
|
||||
crypt_pair.recv.apply_keystream(data);
|
||||
}
|
||||
}
|
||||
|
||||
fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]) {
|
||||
if let Some(crypt_pair) = self.streams.get_mut(substream as usize){
|
||||
if let Some(crypt_pair) = self.streams.get_mut(substream as usize) {
|
||||
crypt_pair.send.apply_keystream(data);
|
||||
}
|
||||
}
|
||||
|
|
@ -182,10 +123,14 @@ impl CryptoHandlerConnectionInstance for SecureInstance {
|
|||
|
||||
fn sign_packet(&self, packet: &mut PRUDPV1Packet) {
|
||||
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 {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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::{
|
||||
ConnectionSignature, FragmentId, MaximumSubstreamId, SupportedFunctions,
|
||||
};
|
||||
use crate::prudp::packet::{PRUDPV1Header, PRUDPV1Packet, TypesFlags, VirtualPort};
|
||||
use crate::prudp::sockaddr::PRUDPSockAddr;
|
||||
use crate::prudp::packet::{PRUDPV1Header, PRUDPV1Packet};
|
||||
use async_trait::async_trait;
|
||||
use log::info;
|
||||
use log::error;
|
||||
use log::{info, warn};
|
||||
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::io::Cursor;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
use std::sync::{Arc, Weak};
|
||||
use v_byte_helpers::ReadExtensions;
|
||||
use v_byte_helpers::little_endian::read_u16;
|
||||
|
||||
use std::time::Duration;
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::sync::mpsc::{channel, Receiver, Sender};
|
||||
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 ;-;
|
||||
// (maybe i will fix that some day)
|
||||
|
||||
/// 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 user_id: u32,
|
||||
pub socket_addr: PRUDPSockAddr,
|
||||
|
|
@ -50,12 +41,14 @@ struct InternalConnection<E: CryptoHandlerConnectionInstance> {
|
|||
connections: Weak<Mutex<BTreeMap<PRUDPSockAddr, Arc<Mutex<InternalConnection<E>>>>>>,
|
||||
reliable_server_counter: u16,
|
||||
reliable_client_counter: u16,
|
||||
supported_function_version: u32,
|
||||
// maybe add connection id(need to see if its even needed)
|
||||
crypto_handler_instance: E,
|
||||
data_sender: Sender<Vec<u8>>,
|
||||
socket: Arc<UdpSocket>,
|
||||
packet_queue: HashMap<u16, PRUDPV1Packet>,
|
||||
last_packet_time: Instant,
|
||||
unacknowleged_packets: Vec<(Instant, PRUDPV1Packet)>,
|
||||
}
|
||||
|
||||
impl<E: CryptoHandlerConnectionInstance> Deref for InternalConnection<E> {
|
||||
|
|
@ -66,6 +59,7 @@ impl<E: CryptoHandlerConnectionInstance> Deref for 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 {
|
||||
let prev_val = self.reliable_server_counter;
|
||||
let (val, _) = self.reliable_server_counter.overflowing_add(1);
|
||||
|
|
@ -74,20 +68,30 @@ impl<E: CryptoHandlerConnectionInstance> InternalConnection<E> {
|
|||
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]
|
||||
async fn send_raw_packet(&self, mut prudp_packet: PRUDPV1Packet) {
|
||||
prudp_packet.set_sizes();
|
||||
async fn send_raw_packet(&self, prudp_packet: &PRUDPV1Packet) {
|
||||
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
|
||||
.write_to(&mut vec)
|
||||
.expect("somehow failed to convert backet to bytes");
|
||||
let mut conns = conns.lock().await;
|
||||
|
||||
self.socket
|
||||
.send_to(&vec, self.socket_addr.regular_socket_addr)
|
||||
.await
|
||||
.expect("failed to send data back");
|
||||
conns.remove(&self.socket_addr);
|
||||
|
||||
// the connection will now drop as soon as we leave this due to no longer having a permanent
|
||||
// reference
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -195,7 +199,9 @@ impl<T: CryptoHandlerConnectionInstance> AnyInternalConnection for InternalConne
|
|||
|
||||
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) {
|
||||
|
|
@ -222,23 +228,29 @@ impl<T: CryptoHandlerConnectionInstance> AnyInternalConnection for InternalConne
|
|||
|
||||
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 {
|
||||
// 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
|
||||
self.delete_connection().await;
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
async fn get_connection(
|
||||
&self,
|
||||
|
|
@ -256,19 +268,12 @@ impl<T: CryptoHandler> InternalSocket<T> {
|
|||
Some(conn)
|
||||
}
|
||||
|
||||
async fn send_packet_unbuffered(&self, dest: PRUDPSockAddr, mut packet: PRUDPV1Packet) {
|
||||
packet.set_sizes();
|
||||
|
||||
let mut vec = Vec::new();
|
||||
|
||||
packet
|
||||
.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");
|
||||
/// sends a raw packet to a specific prudp socket address
|
||||
///
|
||||
/// a raw packet is a packet is a packet which wont get processed any further,
|
||||
/// sizes signatures etc need to be set before using this function
|
||||
async fn send_packet_unbuffered(&self, dest: PRUDPSockAddr, packet: &PRUDPV1Packet) {
|
||||
send_raw_prudp_to_sockaddr(&self.socket, dest, packet).await
|
||||
}
|
||||
|
||||
async fn handle_syn(&self, address: PRUDPSockAddr, packet: PRUDPV1Packet) {
|
||||
|
|
@ -303,7 +308,7 @@ impl<T: CryptoHandler> InternalSocket<T> {
|
|||
|
||||
//println!("got syn: {:?}", response);
|
||||
|
||||
self.send_packet_unbuffered(address, response).await;
|
||||
self.send_packet_unbuffered(address, &response).await;
|
||||
}
|
||||
|
||||
async fn connection_thread(
|
||||
|
|
@ -315,7 +320,7 @@ impl<T: CryptoHandler> InternalSocket<T> {
|
|||
let mut conn = conn.lock().await;
|
||||
|
||||
if conn.last_packet_time < (Instant::now() - Duration::from_secs(5)) {
|
||||
conn.send_raw_packet(PRUDPV1Packet {
|
||||
conn.send_raw_packet(&PRUDPV1Packet {
|
||||
header: PRUDPV1Header {
|
||||
sequence_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)) {
|
||||
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);
|
||||
|
||||
sleep(Duration::from_secs(5)).await;
|
||||
sleep(Duration::from_millis(500)).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -347,6 +367,7 @@ impl<T: CryptoHandler> InternalSocket<T> {
|
|||
socket_addr: PRUDPSockAddr,
|
||||
session_id: u8,
|
||||
is_instantiator: bool,
|
||||
supported_function_version: u32,
|
||||
) {
|
||||
let common = Arc::new(CommonConnection {
|
||||
user_id: crypto_handler_instance.get_user_id(),
|
||||
|
|
@ -367,6 +388,8 @@ impl<T: CryptoHandler> InternalSocket<T> {
|
|||
socket: self.socket.clone(),
|
||||
packet_queue: Default::default(),
|
||||
last_packet_time: Instant::now(),
|
||||
unacknowleged_packets: Vec::new(),
|
||||
supported_function_version,
|
||||
};
|
||||
|
||||
let internal = Arc::new(Mutex::new(internal));
|
||||
|
|
@ -444,12 +467,17 @@ impl<T: CryptoHandler> InternalSocket<T> {
|
|||
.options
|
||||
.push(ConnectionSignature(Default::default()));
|
||||
|
||||
let mut functions: u32 = 0;
|
||||
|
||||
for option in &packet.options {
|
||||
match option {
|
||||
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);
|
||||
|
||||
self.create_connection(crypto, address, session_id, false)
|
||||
self.create_connection(crypto, address, session_id, false, functions)
|
||||
.await;
|
||||
|
||||
self.send_packet_unbuffered(address, response).await;
|
||||
self.send_packet_unbuffered(address, &response).await;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
self.send_packet_unbuffered(address, response).await;
|
||||
self.send_packet_unbuffered(address, &response).await;
|
||||
|
||||
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);
|
||||
|
||||
self.send_packet_unbuffered(address, response).await;
|
||||
self.send_packet_unbuffered(address, &response).await;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
self.send_packet_unbuffered(address, response.clone()).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;
|
||||
self.send_packet_unbuffered(address, &response).await;
|
||||
|
||||
conn.delete_connection().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 {
|
||||
info!("got ack");
|
||||
|
||||
|
||||
if packet.header.types_and_flags.get_types() == SYN
|
||||
|| packet.header.types_and_flags.get_types() == CONNECT
|
||||
{
|
||||
|
|
@ -596,13 +624,76 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
|
|||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -630,7 +721,7 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
|
|||
|
||||
let remote_signature = address.calculate_connection_signature();
|
||||
|
||||
let packet = PRUDPV1Packet {
|
||||
let mut packet = PRUDPV1Packet {
|
||||
header: PRUDPV1Header {
|
||||
source_port: self.virtual_port,
|
||||
destination_port: address.virtual_port,
|
||||
|
|
@ -645,7 +736,9 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
|
|||
..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 {
|
||||
error!("what");
|
||||
|
|
@ -661,7 +754,7 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
|
|||
return None;
|
||||
};
|
||||
|
||||
let packet = PRUDPV1Packet {
|
||||
let mut packet = PRUDPV1Packet {
|
||||
header: PRUDPV1Header {
|
||||
source_port: self.virtual_port,
|
||||
destination_port: address.virtual_port,
|
||||
|
|
@ -676,9 +769,11 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
|
|||
..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");
|
||||
return None;
|
||||
};
|
||||
|
|
@ -688,7 +783,7 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
|
|||
.instantiate(remote_signature, *own_signature, &[], 1)?;
|
||||
|
||||
//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(())
|
||||
}
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
use crate::prudp::packet::PRUDPV1Packet;
|
||||
use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance};
|
||||
use once_cell::sync::Lazy;
|
||||
use rc4::{Key, KeyInit, Rc4, StreamCipher};
|
||||
use rnex_core::prudp::encryption::{DEFAULT_KEY, EncryptionPair};
|
||||
use typenum::U5;
|
||||
use crate::prudp::packet::PRUDPV1Packet;
|
||||
use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance, EncryptionPair};
|
||||
|
||||
pub struct Unsecure(pub &'static str);
|
||||
|
||||
|
||||
|
||||
pub struct UnsecureInstance {
|
||||
key: &'static str,
|
||||
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
|
||||
// only runs once and so that i can put it here as a "constant" (for performance and readability)
|
||||
// since for some reason rust crypto doesn't have any const time key initialization
|
||||
static DEFAULT_KEY: Lazy<Key<U5>> = Lazy::new(|| Key::from(*b"CD&ML"));
|
||||
|
||||
impl CryptoHandler for Unsecure {
|
||||
type CryptoConnectionInstance = UnsecureInstance;
|
||||
|
|
@ -53,13 +51,13 @@ impl CryptoHandlerConnectionInstance for UnsecureInstance {
|
|||
type Encryption = Rc4<U5>;
|
||||
|
||||
fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]) {
|
||||
if let Some(crypt_pair) = self.streams.get_mut(substream as usize){
|
||||
if let Some(crypt_pair) = self.streams.get_mut(substream as usize) {
|
||||
crypt_pair.recv.apply_keystream(data);
|
||||
}
|
||||
}
|
||||
|
||||
fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]) {
|
||||
if let Some(crypt_pair) = self.streams.get_mut(substream as usize){
|
||||
if let Some(crypt_pair) = self.streams.get_mut(substream as usize) {
|
||||
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));
|
||||
}
|
||||
|
||||
fn verify_packet(&self, packet: &PRUDPV1Packet) -> bool {
|
||||
fn verify_packet(&self, _packet: &PRUDPV1Packet) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
53
rnex-core/Cargo.toml
Normal file
53
rnex-core/Cargo.toml
Normal 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"
|
||||
99
rnex-core/benches/rmc_serialization.rs
Normal file
99
rnex-core/benches/rmc_serialization.rs
Normal 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);
|
||||
|
||||
47
rnex-core/src/executables/backend_server_insecure.rs
Normal file
47
rnex-core/src/executables/backend_server_insecure.rs
Normal 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;
|
||||
}
|
||||
23
rnex-core/src/executables/backend_server_secure.rs
Normal file
23
rnex-core/src/executables/backend_server_secure.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
88
rnex-core/src/executables/common.rs
Normal file
88
rnex-core/src/executables/common.rs
Normal 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));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +1,16 @@
|
|||
use std::io::Cursor;
|
||||
use std::net::SocketAddrV4;
|
||||
use std::sync::{Arc, Weak};
|
||||
use log::error;
|
||||
use macros::rmc_struct;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::sync::RwLock;
|
||||
use rust_nex::common::setup;
|
||||
use rust_nex::executables::common::{OWN_IP_PRIVATE, SERVER_PORT};
|
||||
use rust_nex::reggie::{EdgeNodeHolderConnectOption, EdgeNodeManagement, LocalEdgeNodeHolder};
|
||||
use rust_nex::rmc::protocols::new_rmc_gateway_connection;
|
||||
use rust_nex::rmc::response::ErrorCode;
|
||||
use rust_nex::util::SplittableBufferConnection;
|
||||
use rust_nex::rmc::structures::RmcSerialize;
|
||||
use rnex_core::common::setup;
|
||||
use rnex_core::executables::common::{OWN_IP_PRIVATE, SERVER_PORT};
|
||||
use rnex_core::reggie::{EdgeNodeHolderConnectOption, EdgeNodeManagement, LocalEdgeNodeHolder};
|
||||
use rnex_core::rmc::protocols::new_rmc_gateway_connection;
|
||||
use rnex_core::rmc::response::ErrorCode;
|
||||
use rnex_core::util::SplittableBufferConnection;
|
||||
use rnex_core::rmc::structures::RmcSerialize;
|
||||
|
||||
#[rmc_struct(EdgeNodeHolder)]
|
||||
struct EdgeNode{
|
||||
|
|
@ -56,7 +55,7 @@ async fn main() {
|
|||
|
||||
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 Some(data) = conn.recv().await else {
|
||||
22
rnex-core/src/executables/friends_backend.rs
Normal file
22
rnex-core/src/executables/friends_backend.rs
Normal 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;
|
||||
}
|
||||
3
rnex-core/src/executables/mod.rs
Normal file
3
rnex-core/src/executables/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pub mod common;
|
||||
pub mod friends_backend;
|
||||
pub mod regular_backend;
|
||||
33
rnex-core/src/executables/regular_backend.rs
Normal file
33
rnex-core/src/executables/regular_backend.rs
Normal 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;
|
||||
}
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
use std::{env, result};
|
||||
use std::array::TryFromSliceError;
|
||||
use std::str::FromStr;
|
||||
use std::ops::Deref;
|
||||
use json::{object, JsonValue};
|
||||
use once_cell::sync::Lazy;
|
||||
use reqwest::{Body, Method, Url};
|
||||
use reqwest::header::HeaderValue;
|
||||
use thiserror::Error;
|
||||
use tokio::task::{spawn_blocking, JoinError};
|
||||
use crate::grpc::account::Error::SomethingHappened;
|
||||
static API_KEY: Lazy<String> = Lazy::new(||{
|
||||
let key = env::var("ACCOUNT_GQL_API_KEY")
|
||||
|
|
@ -26,27 +25,38 @@ static CLIENT_URI: Lazy<String> = Lazy::new(||{
|
|||
#[derive(Error, Debug)]
|
||||
pub enum Error{
|
||||
#[error(transparent)]
|
||||
Creation(#[from] reqwest::Error),
|
||||
RequestError(#[from] ureq::Error),
|
||||
#[error(transparent)]
|
||||
Json(#[from] json::Error),
|
||||
#[error(transparent)]
|
||||
Status(#[from] tonic::Status),
|
||||
//#[error(transparent)]
|
||||
//Status(#[from] tonic::Status),
|
||||
#[error("invalid password size: {0}")]
|
||||
PasswordConversion(#[from] TryFromSliceError),
|
||||
#[error("something happened")]
|
||||
SomethingHappened
|
||||
SomethingHappened,
|
||||
#[error("error joining blocking task: {0}")]
|
||||
Join(#[from] JoinError)
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
pub struct Client(reqwest::Client);
|
||||
pub struct Client;//(reqwest::Client);
|
||||
|
||||
impl Client{
|
||||
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>{
|
||||
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());
|
||||
|
||||
*(request.body_mut()) = Some(Body::from(request_data.to_string()));
|
||||
|
|
@ -56,6 +66,8 @@ impl Client{
|
|||
let response = self.0.execute(request).await?;
|
||||
|
||||
Ok(json::parse(&response.text().await?)?)
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
pub async fn get_nex_password(&mut self , pid: u32) -> Result<[u8; 16]>{
|
||||
|
|
@ -136,21 +148,4 @@ impl Client{
|
|||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
|
|
@ -2,8 +2,7 @@
|
|||
//! before account rs is finished.
|
||||
//!
|
||||
//! 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)>;
|
||||
mod protobufs;
|
||||
//type InterceptorFunc = Box<dyn Fn(Request<()>) -> Result<Request<()>, Status> + Send>;
|
||||
pub mod account;
|
||||
|
|
@ -1,23 +1,41 @@
|
|||
use std::io::{Read, Write};
|
||||
use bytemuck::{bytes_of, Pod, Zeroable};
|
||||
use bytemuck::{Pod, Zeroable, bytes_of};
|
||||
use cfg_if::cfg_if;
|
||||
use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc};
|
||||
use hmac::Hmac;
|
||||
use hmac::Mac;
|
||||
use md5::{Digest, Md5};
|
||||
use rc4::{Rc4, Rc4Core, StreamCipher};
|
||||
use rc4::KeyInit;
|
||||
use rc4::cipher::StreamCipherCoreWrapper;
|
||||
use rc4::consts::U16;
|
||||
use hmac::Mac;
|
||||
use rc4::KeyInit;
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
use rc4::{Rc4, Rc4Core, StreamCipher};
|
||||
use rnex_core::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>;
|
||||
|
||||
pub fn derive_key(pid: u32, password: [u8; 16]) -> [u8; 16]{
|
||||
let iteration_count = 65000 + pid%1024;
|
||||
pub fn derive_key(pid: u32, password: &[u8]) -> [u8; 16] {
|
||||
let iteration_count = 65000 + pid % 1024;
|
||||
// we do one iteration out here to ensure the key is always 16 bytes
|
||||
|
||||
let mut key = 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();
|
||||
md5.update(key);
|
||||
key = md5.finalize().try_into().unwrap();
|
||||
|
|
@ -29,12 +47,12 @@ pub fn derive_key(pid: u32, password: [u8; 16]) -> [u8; 16]{
|
|||
#[repr(transparent)]
|
||||
pub struct KerberosDateTime(pub u64);
|
||||
|
||||
impl KerberosDateTime{
|
||||
pub fn new(second: u64, minute: u64, hour: u64, day: u64, month: u64, year:u64 ) -> Self {
|
||||
impl KerberosDateTime {
|
||||
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))
|
||||
}
|
||||
|
||||
pub fn now() -> Self{
|
||||
pub fn now() -> Self {
|
||||
let now = chrono::Utc::now();
|
||||
Self::new(
|
||||
now.second() as u64,
|
||||
|
|
@ -47,69 +65,80 @@ impl KerberosDateTime{
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_seconds(&self) -> u8{
|
||||
pub fn get_seconds(&self) -> u8 {
|
||||
(self.0 & 0b111111) as u8
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_minutes(&self) -> u8{
|
||||
pub fn get_minutes(&self) -> u8 {
|
||||
((self.0 >> 6) & 0b111111) as u8
|
||||
}
|
||||
#[inline]
|
||||
pub fn get_hours(&self) -> u8{
|
||||
pub fn get_hours(&self) -> u8 {
|
||||
((self.0 >> 12) & 0b111111) as u8
|
||||
}
|
||||
#[inline]
|
||||
pub fn get_days(&self) -> u8{
|
||||
pub fn get_days(&self) -> u8 {
|
||||
((self.0 >> 17) & 0b111111) as u8
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_month(&self) -> u8{
|
||||
pub fn get_month(&self) -> u8 {
|
||||
((self.0 >> 22) & 0b1111) as u8
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_year(&self) -> u64{
|
||||
pub fn get_year(&self) -> u64 {
|
||||
(self.0 >> 26) & 0xFFFFFFFF
|
||||
}
|
||||
|
||||
pub fn to_regular_time(&self) -> chrono::DateTime<Utc>{
|
||||
pub fn to_regular_time(&self) -> chrono::DateTime<Utc> {
|
||||
NaiveDateTime::new(
|
||||
NaiveDate::from_ymd_opt(self.get_year() as i32, 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()
|
||||
NaiveDate::from_ymd_opt(
|
||||
self.get_year() as i32,
|
||||
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{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> {
|
||||
impl RmcSerialize for KerberosDateTime {
|
||||
fn serialize(&self, writer: &mut impl Write) -> Result<()> {
|
||||
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)?))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Pod, Zeroable, Copy, Clone)]
|
||||
#[repr(C, packed)]
|
||||
pub struct TicketInternalData{
|
||||
pub struct TicketInternalData {
|
||||
pub issued_time: KerberosDateTime,
|
||||
pub pid: u32,
|
||||
pub session_key: [u8; 32],
|
||||
pub session_key: [u8; SESSION_KEY_LENGTH],
|
||||
}
|
||||
|
||||
impl TicketInternalData{
|
||||
pub(crate) fn new(pid: u32) -> Self{
|
||||
Self{
|
||||
impl TicketInternalData {
|
||||
pub(crate) fn new(pid: u32) -> Self {
|
||||
Self {
|
||||
issued_time: KerberosDateTime::now(),
|
||||
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 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();
|
||||
|
||||
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()[..];
|
||||
|
||||
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()
|
||||
}
|
||||
|
|
@ -129,42 +160,44 @@ impl TicketInternalData{
|
|||
|
||||
#[derive(Pod, Zeroable, Copy, Clone)]
|
||||
#[repr(C, packed)]
|
||||
pub struct Ticket{
|
||||
pub session_key: [u8; 32],
|
||||
pub struct Ticket {
|
||||
pub session_key: [u8; SESSION_KEY_LENGTH],
|
||||
pub pid: u32,
|
||||
}
|
||||
|
||||
impl Ticket{
|
||||
pub(crate) fn encrypt(&self, key: [u8; 16], internal_data: &[u8]) -> Box<[u8]>{
|
||||
impl Ticket {
|
||||
pub fn encrypt(&self, key: [u8; 16], internal_data: &[u8]) -> Box<[u8]> {
|
||||
let mut data = bytes_of(self).to_vec();
|
||||
|
||||
internal_data.serialize(&mut data).expect("unable to write to vec");
|
||||
internal_data
|
||||
.serialize(&mut data)
|
||||
.expect("unable to write to vec");
|
||||
|
||||
let mut rc4: StreamCipherCoreWrapper<Rc4Core<U16>> = Rc4::new_from_slice(&key).unwrap();
|
||||
rc4.apply_keystream(&mut data);
|
||||
|
||||
let mut hmac = <Md5Hmac as KeyInit>::new_from_slice(&key).unwrap();
|
||||
|
||||
hmac.write_all(&data[..]).expect("failed to write data to hmac");
|
||||
hmac.write_all(&data[..])
|
||||
.expect("failed to write data to hmac");
|
||||
|
||||
let hmac_result = &hmac.finalize().into_bytes()[..];
|
||||
|
||||
data.write_all(&hmac_result).expect("failed to write data to vec");
|
||||
data.write_all(&hmac_result)
|
||||
.expect("failed to write data to vec");
|
||||
|
||||
data.into_boxed_slice()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test{
|
||||
use chrono::{Datelike, Utc};
|
||||
mod test {
|
||||
use crate::kerberos::KerberosDateTime;
|
||||
|
||||
#[test]
|
||||
fn kerberos_time_convert_test(){
|
||||
fn kerberos_time_convert_test() {
|
||||
let time = KerberosDateTime(135904948834);
|
||||
|
||||
println!("{}", time.to_regular_time().to_rfc2822());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,9 +6,8 @@
|
|||
|
||||
|
||||
|
||||
extern crate self as rust_nex;
|
||||
extern crate self as rnex_core;
|
||||
|
||||
pub mod endianness;
|
||||
pub mod prudp;
|
||||
pub mod rmc;
|
||||
//mod protocols;
|
||||
|
|
@ -18,9 +17,13 @@ pub mod kerberos;
|
|||
pub mod nex;
|
||||
pub mod result;
|
||||
pub mod versions;
|
||||
pub mod web;
|
||||
pub mod common;
|
||||
pub mod reggie;
|
||||
pub mod rnex_proxy_common;
|
||||
pub mod util;
|
||||
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
127
rnex-core/src/main.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
32
rnex-core/src/nex/account.rs
Normal file
32
rnex-core/src/nex/account.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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::net::SocketAddrV4;
|
||||
use std::sync::Arc;
|
||||
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;
|
||||
use std::sync::{Arc, LazyLock, OnceLock};
|
||||
|
||||
define_rmc_proto!(
|
||||
proto AuthClientProtocol{
|
||||
|
|
@ -30,8 +33,8 @@ pub struct AuthHandler {
|
|||
}
|
||||
|
||||
pub fn generate_ticket(
|
||||
source_act_login_data: (u32, [u8; 16]),
|
||||
dest_act_login_data: (u32, [u8; 16]),
|
||||
source_act_login_data: (u32, &[u8]),
|
||||
dest_act_login_data: (u32, &[u8]),
|
||||
) -> Box<[u8]> {
|
||||
let source_key = derive_key(source_act_login_data.0, source_act_login_data.1);
|
||||
let dest_key = derive_key(dest_act_login_data.0, dest_act_login_data.1);
|
||||
|
|
@ -48,7 +51,13 @@ pub fn generate_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 {
|
||||
return None;
|
||||
};
|
||||
|
|
@ -57,19 +66,96 @@ async fn get_login_data_by_pid(pid: u32) -> Option<(u32, [u8; 16])> {
|
|||
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!(
|
||||
"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 {
|
||||
async fn login(&self, _name: String) -> Result<(), ErrorCode> {
|
||||
todo!()
|
||||
async fn login(
|
||||
&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(
|
||||
|
|
@ -77,30 +163,16 @@ impl Auth for AuthHandler {
|
|||
name: String,
|
||||
_extra_data: Any,
|
||||
) -> Result<(QResult, u32, Vec<u8>, ConnectionData, String), ErrorCode> {
|
||||
let Ok(pid) = name.parse() else {
|
||||
return Err(ErrorCode::Core_InvalidArgument);
|
||||
};
|
||||
|
||||
let Ok(mut client) = account::Client::new().await else {
|
||||
return Err(ErrorCode::Core_Exception);
|
||||
};
|
||||
|
||||
let Ok(passwd) = client.get_nex_password(pid).await else {
|
||||
return Err(ErrorCode::Core_Exception);
|
||||
};
|
||||
|
||||
let source_login_data = (pid, passwd);
|
||||
let destination_login_data = self.destination_server_acct.get_login_data();
|
||||
|
||||
let ticket = generate_ticket(source_login_data, destination_login_data);
|
||||
let (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);
|
||||
};
|
||||
|
||||
|
|
@ -112,13 +184,16 @@ impl Auth for AuthHandler {
|
|||
special_protocols: Vec::new(),
|
||||
};
|
||||
|
||||
Ok((
|
||||
let ret = (
|
||||
result,
|
||||
source_login_data.0,
|
||||
pid,
|
||||
ticket.into(),
|
||||
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(
|
||||
|
|
@ -126,22 +201,19 @@ impl Auth for AuthHandler {
|
|||
source_pid: u32,
|
||||
destination_pid: u32,
|
||||
) -> Result<(QResult, Vec<u8>), ErrorCode> {
|
||||
let Some(source_login_data) = get_login_data_by_pid(source_pid).await else {
|
||||
let Some((pid, passwd)) = get_login_data_by_pid(source_pid).await else {
|
||||
return Err(ErrorCode::Core_Exception);
|
||||
};
|
||||
|
||||
let desgination_login_data = if destination_pid == self.destination_server_acct.pid {
|
||||
self.destination_server_acct.get_login_data()
|
||||
} else {
|
||||
let Some(login) = get_login_data_by_pid(destination_pid).await else {
|
||||
return Err(ErrorCode::Core_Exception);
|
||||
};
|
||||
login
|
||||
return Err(ErrorCode::RendezVous_InvalidOperation);
|
||||
};
|
||||
|
||||
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()))
|
||||
}
|
||||
|
|
@ -157,30 +229,30 @@ impl Auth for AuthHandler {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::rmc::structures::connection_data::ConnectionData;
|
||||
use crate::rmc::structures::qresult::QResult;
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
use crate::rmc::response::RMCResponse;
|
||||
use rnex_core::rmc::response::RMCResponse;
|
||||
use rnex_core::rmc::structures::RmcSerialize;
|
||||
use rnex_core::rmc::structures::connection_data::ConnectionData;
|
||||
use rnex_core::rmc::structures::qresult::QResult;
|
||||
use std::io::Cursor;
|
||||
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
|
||||
return;
|
||||
let stuff = hex::decode("200100000a0106000000028000000100010051b3995774000000a6321c7f78847c1c5e9fb825eb26bd91841f1a40d92fc694159666119cb13527f1463ac48ad42a63e6613ede67041554b1770978112e6f1f3e177a2bfc75933216dbe38f70133a1eb28e2ae32a4b5c4b0c3e3efd4c02907992e259b257270b57a9dbe7792f4721b07f8fafb9e32d50f2555c616a015c0000004b007072756470733a2f5049443d323b7369643d313b73747265616d3d31303b747970653d323b616464726573733d322e3234332e39352e3131333b706f72743d31303030313b4349443d3100000000000100002c153ba51f00000033006272616e63683a6f726967696e2f70726f6a6563742f7775702d61676d6a206275696c643a335f385f31355f323030345f3000").unwrap();
|
||||
let stuff = RMCResponse::new(&mut Cursor::new(stuff)).unwrap();
|
||||
|
||||
let crate::rmc::response::RMCResponseResult::Success { call_id, method_id, data: stuff} = stuff.response_result else {
|
||||
let rnex_core::rmc::response::RMCResponseResult::Success { .. } = stuff.response_result
|
||||
else {
|
||||
panic!()
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 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(
|
||||
&mut Cursor::new(stuff),
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
println!("data: {:?}", data);
|
||||
}
|
||||
93
rnex-core/src/nex/common.rs
Normal file
93
rnex-core/src/nex/common.rs
Normal 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])
|
||||
}
|
||||
160
rnex-core/src/nex/friends_handler.rs
Normal file
160
rnex-core/src/nex/friends_handler.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
481
rnex-core/src/nex/matchmake.rs
Normal file
481
rnex-core/src/nex/matchmake.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
pub mod account;
|
||||
pub mod auth_handler;
|
||||
pub mod user;
|
||||
pub mod common;
|
||||
pub mod friends_handler;
|
||||
pub mod matchmake;
|
||||
pub mod remote_console;
|
||||
pub mod matchmake;
|
||||
pub mod user;
|
||||
|
|
@ -1,33 +1,43 @@
|
|||
use crate::define_rmc_proto;
|
||||
use crate::nex::common::get_station_urls;
|
||||
use crate::nex::matchmake::{ExtendedMatchmakeSession, MatchmakeManager};
|
||||
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::{
|
||||
Matchmake, RawMatchmake, RawMatchmakeInfo, RemoteMatchmake,
|
||||
};
|
||||
use crate::rmc::protocols::ranking::{Ranking, RawRanking, RawRankingInfo, RemoteRanking};
|
||||
use crate::rmc::protocols::matchmake_extension::{
|
||||
use crate::rmc::protocols::nat_traversal::{
|
||||
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,
|
||||
};
|
||||
use crate::rmc::protocols::nat_traversal::{NatTraversal, RawNatTraversal, RawNatTraversalInfo, RemoteNatTraversal, RemoteNatTraversalConsole};
|
||||
use crate::rmc::protocols::secure::{RawSecure, RawSecureInfo, RemoteSecure, Secure};
|
||||
use crate::rmc::protocols::matchmake_ext::{MatchmakeExt, RawMatchmakeExt, RawMatchmakeExtInfo, RemoteMatchmakeExt};
|
||||
use crate::rmc::response::ErrorCode;
|
||||
use crate::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession};
|
||||
use rnex_core::rmc::protocols::ranking::{Ranking, RawRanking, RawRankingInfo, RemoteRanking};
|
||||
use rnex_core::rmc::protocols::secure::{RawSecure, RawSecureInfo, RemoteSecure, Secure};
|
||||
use rnex_core::rmc::response::ErrorCode;
|
||||
use rnex_core::rmc::structures::any::Any;
|
||||
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::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!(
|
||||
proto UserProtocol{
|
||||
|
|
@ -63,98 +73,27 @@ impl Secure for User {
|
|||
users.insert(cid, self.this.clone());
|
||||
drop(users);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 stations = get_station_urls(&station_urls, self.ip, self.pid, cid).await?;
|
||||
|
||||
let first = stations.first().unwrap().clone();
|
||||
|
||||
let mut lock = self.station_url.write().await;
|
||||
|
||||
*lock = vec![
|
||||
public_station.clone(),
|
||||
// private_station.clone()
|
||||
];
|
||||
*lock = stations;
|
||||
|
||||
drop(lock);
|
||||
|
||||
let result = QResult::success(ErrorCode::Core_Unknown);
|
||||
|
||||
let out = public_station.to_string();
|
||||
Ok((result, cid, first))
|
||||
}
|
||||
|
||||
println!("out: {}", out);
|
||||
|
||||
Ok((result, cid, public_station))
|
||||
async fn register_ex(
|
||||
&self,
|
||||
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> {
|
||||
|
|
@ -169,8 +108,8 @@ impl Secure for User {
|
|||
};
|
||||
|
||||
let Some(replacement_target) = lock.iter_mut().find(|url| {
|
||||
url.options.iter().any(|o| o == target_addr) &&
|
||||
url.options.iter().any(|o| o == target_port)
|
||||
url.options.iter().any(|o| o == target_addr)
|
||||
&& url.options.iter().any(|o| o == target_port)
|
||||
}) else {
|
||||
return Err(ErrorCode::Core_InvalidArgument);
|
||||
};
|
||||
|
|
@ -230,22 +169,34 @@ impl MatchmakeExtension for User {
|
|||
create_session_param.matchmake_session,
|
||||
&self.this.clone(),
|
||||
)
|
||||
.await;
|
||||
.await;
|
||||
|
||||
let mut joining_players = vec![self.this.clone()];
|
||||
|
||||
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 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 {
|
||||
joining_players.push(Arc::downgrade(&player));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
drop(users);
|
||||
|
||||
new_session.session.participation_count = create_session_param.participation_count as u32;
|
||||
|
|
@ -266,20 +217,43 @@ impl MatchmakeExtension for User {
|
|||
&self,
|
||||
join_session_param: JoinMatchmakeSessionParam,
|
||||
) -> 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;
|
||||
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 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 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 {
|
||||
joining_players.push(Arc::downgrade(&player));
|
||||
}
|
||||
|
|
@ -296,17 +270,28 @@ impl MatchmakeExtension for User {
|
|||
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);
|
||||
|
||||
let mut joining_players = vec![self.this.clone()];
|
||||
|
||||
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 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 {
|
||||
joining_players.push(Arc::downgrade(&player));
|
||||
}
|
||||
|
|
@ -333,7 +318,9 @@ impl MatchmakeExtension for User {
|
|||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
|
@ -359,17 +346,26 @@ impl MatchmakeExtension for User {
|
|||
create_matchmake_session_option: 0,
|
||||
matchmake_session,
|
||||
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 = session.lock().await;
|
||||
|
||||
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 mut session = session.lock().await;
|
||||
|
||||
|
|
@ -388,30 +384,30 @@ impl Matchmake for User {
|
|||
|
||||
let session = session.lock().await;
|
||||
|
||||
let urls: Vec<_> =
|
||||
session
|
||||
.connected_players
|
||||
.iter()
|
||||
.filter_map(|v| v.upgrade())
|
||||
.filter(|u| u.pid == session.session.gathering.host_pid)
|
||||
.map(|u| async move {
|
||||
u.station_url.read().await.clone()
|
||||
})
|
||||
.next()
|
||||
.ok_or(ErrorCode::RendezVous_SessionClosed)?
|
||||
.await;
|
||||
|
||||
let urls: Vec<_> = session
|
||||
.connected_players
|
||||
.iter()
|
||||
.filter_map(|v| v.upgrade())
|
||||
.filter(|u| u.pid == session.session.gathering.host_pid)
|
||||
.map(|u| async move { u.station_url.read().await.clone() })
|
||||
.next()
|
||||
.ok_or(ErrorCode::RendezVous_SessionClosed)?
|
||||
.await;
|
||||
|
||||
println!("{:?}", urls);
|
||||
|
||||
if urls.is_empty(){
|
||||
return Err(ErrorCode::RendezVous_NotParticipatedGathering)
|
||||
|
||||
if urls.is_empty() {
|
||||
return Err(ErrorCode::RendezVous_NotParticipatedGathering);
|
||||
}
|
||||
|
||||
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 mut session = session.lock().await;
|
||||
|
||||
|
|
@ -422,40 +418,50 @@ impl Matchmake for User {
|
|||
continue;
|
||||
};
|
||||
|
||||
player.remote.process_notification_event(NotificationEvent {
|
||||
notif_type: 110000,
|
||||
pid_source: self.pid,
|
||||
param_1: gid,
|
||||
param_2: self.pid,
|
||||
param_3: 0,
|
||||
str_param: "".to_string(),
|
||||
}).await;
|
||||
player
|
||||
.remote
|
||||
.process_notification_event(NotificationEvent {
|
||||
notif_type: 110000,
|
||||
pid_source: self.pid,
|
||||
param_1: gid,
|
||||
param_2: self.pid,
|
||||
param_3: 0,
|
||||
str_param: "".to_string(),
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
if change_session_owner {
|
||||
session.session.gathering.owner_pid = self.pid;
|
||||
|
||||
|
||||
for player in &session.connected_players {
|
||||
let Some(player) = player.upgrade() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
player.remote.process_notification_event(NotificationEvent {
|
||||
notif_type: 4000,
|
||||
pid_source: self.pid,
|
||||
param_1: gid,
|
||||
param_2: self.pid,
|
||||
param_3: 0,
|
||||
str_param: "".to_string(),
|
||||
}).await;
|
||||
player
|
||||
.remote
|
||||
.process_notification_event(NotificationEvent {
|
||||
notif_type: 4000,
|
||||
pid_source: self.pid,
|
||||
param_1: gid,
|
||||
param_2: self.pid,
|
||||
param_3: 0,
|
||||
str_param: "".to_string(),
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
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 mut session = session.lock().await;
|
||||
|
||||
|
|
@ -468,14 +474,17 @@ impl Matchmake for User {
|
|||
continue;
|
||||
};
|
||||
|
||||
player.remote.process_notification_event(NotificationEvent {
|
||||
notif_type: 4000,
|
||||
pid_source: self.pid,
|
||||
param_1: gid,
|
||||
param_2: *candidate,
|
||||
param_3: 0,
|
||||
str_param: "".to_string(),
|
||||
}).await;
|
||||
player
|
||||
.remote
|
||||
.process_notification_event(NotificationEvent {
|
||||
notif_type: 4000,
|
||||
pid_source: self.pid,
|
||||
param_1: gid,
|
||||
param_2: *candidate,
|
||||
param_3: 0,
|
||||
str_param: "".to_string(),
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -487,12 +496,12 @@ impl MatchmakeExt for User {
|
|||
let session = self.matchmake_manager.get_session(gid).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)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
impl NatTraversal for User {
|
||||
|
|
@ -507,7 +516,7 @@ impl NatTraversal for User {
|
|||
for station_url in urls.iter_mut() {
|
||||
station_url.options.retain(|o| match o {
|
||||
NatMapping(_) | NatFiltering(_) => false,
|
||||
_ => true
|
||||
_ => true,
|
||||
});
|
||||
|
||||
station_url.options.push(NatMapping(nat_mapping as u8));
|
||||
|
|
@ -517,7 +526,12 @@ impl NatTraversal for User {
|
|||
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(())
|
||||
}
|
||||
|
||||
|
|
@ -526,17 +540,28 @@ impl NatTraversal for User {
|
|||
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;
|
||||
|
||||
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 {
|
||||
let Ok(url) = StationUrl::try_from(target.as_ref()) else {
|
||||
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;
|
||||
};
|
||||
|
||||
|
|
@ -548,7 +573,9 @@ impl NatTraversal for User {
|
|||
continue;
|
||||
};
|
||||
|
||||
user.remote.request_probe_initiation(station_to_probe.clone()).await;
|
||||
user.remote
|
||||
.request_probe_initiation(station_to_probe.clone())
|
||||
.await;
|
||||
}
|
||||
|
||||
info!("finished probing");
|
||||
|
|
@ -557,6 +584,4 @@ impl NatTraversal for User {
|
|||
}
|
||||
}
|
||||
|
||||
impl Ranking for User{
|
||||
|
||||
}
|
||||
impl Ranking for User {}
|
||||
20
rnex-core/src/prudp/encryption.rs
Normal file
20
rnex-core/src/prudp/encryption.rs
Normal 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"));
|
||||
6
rnex-core/src/prudp/mod.rs
Normal file
6
rnex-core/src/prudp/mod.rs
Normal 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;
|
||||
|
|
@ -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::net::SocketAddrV4;
|
||||
use hmac::{Hmac, Mac};
|
||||
use macros::RmcSerialize;
|
||||
use crate::prudp::packet::VirtualPort;
|
||||
|
||||
type Md5Hmac = Hmac<md5::Md5>;
|
||||
|
||||
#[derive(Eq, PartialEq, Hash, Debug, Copy, Clone, Ord, PartialOrd, RmcSerialize)]
|
||||
#[rmc_struct(0)]
|
||||
pub struct PRUDPSockAddr{
|
||||
pub struct PRUDPSockAddr {
|
||||
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{
|
||||
Self{
|
||||
impl PRUDPSockAddr {
|
||||
pub fn new(regular_socket_addr: SocketAddrV4, virtual_port: VirtualPort) -> Self {
|
||||
Self {
|
||||
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 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());
|
||||
|
||||
hmac.write_all(&data).expect("figuring this out was complete ass");
|
||||
let result: [u8; 16] = hmac.finalize().into_bytes()[0..16].try_into().expect("fuck");
|
||||
hmac.write_all(&data)
|
||||
.expect("figuring this out was complete ass");
|
||||
let result: [u8; 16] = hmac.finalize().into_bytes()[0..16]
|
||||
.try_into()
|
||||
.expect("fuck");
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ use std::io::Read;
|
|||
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::rmc::structures::Error::StationUrlInvalid;
|
||||
use crate::rmc::structures::helpers::DummyFormatWriter;
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Type{
|
||||
|
|
@ -111,7 +112,7 @@ impl StationUrl{
|
|||
|
||||
impl TryFrom<&str> for StationUrl{
|
||||
type Error = ();
|
||||
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, ()> {
|
||||
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{
|
||||
fn into(self) -> String {
|
||||
let mut url = match self.url_type{
|
||||
impl Display for StationUrl{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
let url_type_str = match self.url_type{
|
||||
UDP => "udp:/",
|
||||
PRUDP => "prudp:/",
|
||||
PRUDPS => "prudps:/"
|
||||
}.to_owned();
|
||||
};
|
||||
write!(f, "{}",url_type_str)?;
|
||||
|
||||
for option in &self.options{
|
||||
match option{
|
||||
Address(v) => write!(url, "address={}", v).expect("failed to write"),
|
||||
Port(v) => write!(url, "port={}", v).expect("failed to write"),
|
||||
StreamType(v) => write!(url, "stream={}", v).expect("failed to write"),
|
||||
StreamID(v) => write!(url, "sid={}", v).expect("failed to write"),
|
||||
ConnectionID(v) => write!(url, "CID={}", v).expect("failed to write"),
|
||||
PrincipalID(v) => write!(url, "PID={}", v).expect("failed to write"),
|
||||
NatType(v) => write!(url, "type={}", v).expect("failed to write"),
|
||||
NatMapping(v) => write!(url, "natm={}", v).expect("failed to write"),
|
||||
NatFiltering(v) => write!(url, "natf={}", v).expect("failed to write"),
|
||||
UPNP(v) => write!(url, "upnp={}", v).expect("failed to write"),
|
||||
RVConnectionID(v) => write!(url, "RVCID={}", v).expect("failed to write"),
|
||||
Platform(v) => write!(url, "pl={}", v).expect("failed to write"),
|
||||
PMP(v) => write!(url, "pmp={}", v).expect("failed to write"),
|
||||
PID(v) => write!(url, "PID={}", v).expect("failed to write"),
|
||||
Address(v) => write!(f, "address={}", v)?,
|
||||
Port(v) => write!(f, "port={}", v)?,
|
||||
StreamType(v) => write!(f, "stream={}", v)?,
|
||||
StreamID(v) => write!(f, "sid={}", v)?,
|
||||
ConnectionID(v) => write!(f, "CID={}", v)?,
|
||||
PrincipalID(v) => write!(f, "PID={}", v)?,
|
||||
NatType(v) => write!(f, "type={}", v)?,
|
||||
NatMapping(v) => write!(f, "natm={}", v)?,
|
||||
NatFiltering(v) => write!(f, "natf={}", v)?,
|
||||
UPNP(v) => write!(f, "upnp={}", v)?,
|
||||
RVConnectionID(v) => write!(f, "RVCID={}", v)?,
|
||||
Platform(v) => write!(f, "pl={}", v)?,
|
||||
PMP(v) => write!(f, "pmp={}", v)?,
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
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{
|
||||
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)?;
|
||||
|
||||
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();
|
||||
|
||||
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{
|
||||
81
rnex-core/src/prudp/ticket.rs
Normal file
81
rnex-core/src/prudp/ticket.rs
Normal 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))
|
||||
}
|
||||
64
rnex-core/src/prudp/types_flags.rs
Normal file
64
rnex-core/src/prudp/types_flags.rs
Normal 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;
|
||||
}
|
||||
60
rnex-core/src/prudp/virtual_port.rs
Normal file
60
rnex-core/src/prudp/virtual_port.rs
Normal 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
60
rnex-core/src/reggie.rs
Normal 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,
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ use std::io;
|
|||
use std::io::{Read, Seek, Write};
|
||||
use bytemuck::bytes_of;
|
||||
use log::error;
|
||||
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use crate::rmc::response::{ErrorCode, RMCResponseResult};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
|
@ -1,18 +1,20 @@
|
|||
use crate::rmc::response::ErrorCode;
|
||||
use crate::rmc::structures::any::Any;
|
||||
use crate::rmc::structures::connection_data::ConnectionData;
|
||||
use crate::rmc::structures::qresult::QResult;
|
||||
use crate::rmc::structures::connection_data::{ConnectionData, ConnectionDataOld};
|
||||
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))
|
||||
#[rmc_proto(10)]
|
||||
pub trait Auth {
|
||||
/// representation of the `Login` method(for details see the
|
||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
#[method_id(1)]
|
||||
async fn login(&self, name: String) -> Result<(), ErrorCode>;
|
||||
async fn login(
|
||||
&self,
|
||||
name: String,
|
||||
) -> Result<(QResult, u32, Vec<u8>, ConnectionDataOld, String), ErrorCode>;
|
||||
|
||||
/// representation of the `LoginEx` method(for details see the
|
||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||
146
rnex-core/src/rmc/protocols/friends.rs
Normal file
146
rnex-core/src/rmc/protocols/friends.rs
Normal 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,
|
||||
>;
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
use macros::{method_id, rmc_proto};
|
||||
use crate::prudp::station_url::StationUrl;
|
||||
use crate::rmc::response::ErrorCode;
|
||||
use rnex_core::prudp::station_url::StationUrl;
|
||||
use rnex_core::rmc::response::ErrorCode;
|
||||
|
||||
#[rmc_proto(21)]
|
||||
pub trait Matchmake{
|
||||
|
|
@ -8,10 +8,8 @@ pub trait Matchmake{
|
|||
async fn unregister_gathering(&self, gid: u32) -> Result<bool, ErrorCode>;
|
||||
#[method_id(41)]
|
||||
async fn get_session_urls(&self, gid: u32) -> Result<Vec<StationUrl>, ErrorCode>;
|
||||
|
||||
#[method_id(42)]
|
||||
async fn update_session_host(&self, gid: u32, change_owner: bool) -> Result<(), ErrorCode>;
|
||||
|
||||
#[method_id(44)]
|
||||
async fn migrate_gathering_ownership(&self, gid: u32, candidates: Vec<u32>, participants_only: bool) -> Result<(), ErrorCode>;
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
use macros::{method_id, rmc_proto};
|
||||
use crate::rmc::response::ErrorCode;
|
||||
use rnex_core::rmc::response::ErrorCode;
|
||||
|
||||
#[rmc_proto(50)]
|
||||
pub trait MatchmakeExt{
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
use macros::{method_id, rmc_proto};
|
||||
use crate::rmc::response::ErrorCode;
|
||||
use crate::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession};
|
||||
use rnex_core::rmc::response::ErrorCode;
|
||||
use rnex_core::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession};
|
||||
|
||||
#[rmc_proto(109)]
|
||||
pub trait MatchmakeExtension{
|
||||
|
|
@ -1,20 +1,22 @@
|
|||
#![allow(async_fn_in_trait)]
|
||||
|
||||
pub mod auth;
|
||||
pub mod secure;
|
||||
pub mod notifications;
|
||||
pub mod friends;
|
||||
pub mod matchmake;
|
||||
pub mod matchmake_ext;
|
||||
pub mod matchmake_extension;
|
||||
pub mod nat_traversal;
|
||||
pub mod matchmake_ext;
|
||||
pub mod notifications;
|
||||
pub mod ranking;
|
||||
pub mod secure;
|
||||
|
||||
use crate::util::{SendingBufferConnection, SplittableBufferConnection};
|
||||
use crate::result::ResultExtension;
|
||||
use crate::rmc::message::RMCMessage;
|
||||
use crate::rmc::protocols::RemoteCallError::ConnectionBroke;
|
||||
use crate::rmc::response::{ErrorCode, RMCResponse, RMCResponseResult};
|
||||
use crate::rmc::structures;
|
||||
use crate::rmc::structures::RmcSerialize;
|
||||
use crate::util::{SendingBufferConnection, SplittableBufferConnection};
|
||||
use log::{error, info};
|
||||
use std::collections::HashMap;
|
||||
use std::future::Future;
|
||||
|
|
@ -24,8 +26,7 @@ use std::sync::Arc;
|
|||
use std::time::Duration;
|
||||
use thiserror::Error;
|
||||
use tokio::sync::{Mutex, Notify};
|
||||
use tokio::time::{sleep, sleep_until, Instant};
|
||||
use crate::result::ResultExtension;
|
||||
use tokio::time::{Instant, sleep, sleep_until};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum RemoteCallError {
|
||||
|
|
@ -68,7 +69,7 @@ impl RmcConnection {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn disconnect(&self){
|
||||
pub async fn disconnect(&self) {
|
||||
self.0.disconnect().await;
|
||||
}
|
||||
}
|
||||
|
|
@ -92,15 +93,11 @@ impl RmcResponseReceiver {
|
|||
let mut locked = self.1.lock().await;
|
||||
|
||||
if let Some(v) = locked.remove(&call_id) {
|
||||
match v.response_result{
|
||||
RMCResponseResult::Success {
|
||||
data,
|
||||
..
|
||||
} => return Ok(data),
|
||||
RMCResponseResult::Error {
|
||||
error_code,
|
||||
..
|
||||
} => return Err(RemoteCallError::ServerError(error_code))
|
||||
match v.response_result {
|
||||
RMCResponseResult::Success { data, .. } => return Ok(data),
|
||||
RMCResponseResult::Error { error_code, .. } => {
|
||||
return Err(RemoteCallError::ServerError(error_code));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -153,8 +150,9 @@ macro_rules! define_rmc_proto {
|
|||
$($protocol:path),*
|
||||
}) => {
|
||||
paste::paste!{
|
||||
#[allow(unused_variables)]
|
||||
pub trait [<Local $name>]: std::any::Any $( + [<Raw $protocol>] + $protocol)* {
|
||||
async fn rmc_call(&self, remote_response_connection: &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{
|
||||
$(
|
||||
[<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>]{
|
||||
fn new(conn: rust_nex::rmc::protocols::RmcConnection) -> Self{
|
||||
impl rnex_core::rmc::protocols::RmcPureRemoteObject for [<Remote $name>]{
|
||||
fn new(conn: rnex_core::rmc::protocols::RmcConnection) -> Self{
|
||||
Self(conn)
|
||||
}
|
||||
}
|
||||
|
||||
impl rnex_core::rmc::protocols::RemoteDisconnectable for [<Remote $name>]{
|
||||
|
||||
async fn disconnect(&self){
|
||||
self.0.disconnect().await;
|
||||
}
|
||||
}
|
||||
|
||||
impl rust_nex::rmc::protocols::HasRmcConnection for [<Remote $name>]{
|
||||
fn get_connection(&self) -> &rust_nex::rmc::protocols::RmcConnection{
|
||||
impl rnex_core::rmc::protocols::HasRmcConnection for [<Remote $name>]{
|
||||
fn get_connection(&self) -> &rnex_core::rmc::protocols::RmcConnection{
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
|
@ -202,14 +204,23 @@ impl RmcCallable for () {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait RemoteInstantiatable{
|
||||
pub trait RmcPureRemoteObject {
|
||||
fn new(conn: RmcConnection) -> Self;
|
||||
}
|
||||
|
||||
pub trait RemoteDisconnectable {
|
||||
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;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
|
|
@ -217,20 +228,23 @@ impl<T: RemoteInstantiatable> Deref for OnlyRemote<T>{
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: RemoteInstantiatable> OnlyRemote<T>{
|
||||
pub fn new(conn: RmcConnection) -> Self{
|
||||
Self(T::new(conn))
|
||||
}
|
||||
|
||||
impl<T: RemoteDisconnectable> OnlyRemote<T> {
|
||||
pub async fn disconnect(&self) {
|
||||
self.0.disconnect().await;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RemoteInstantiatable> 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 {
|
||||
impl<T: RemoteDisconnectable> RmcCallable for OnlyRemote<T> {
|
||||
fn rmc_call(
|
||||
&self,
|
||||
_responder: &SendingBufferConnection,
|
||||
_protocol_id: u16,
|
||||
_method_id: u32,
|
||||
_call_id: u32,
|
||||
_rest: Vec<u8>,
|
||||
) -> impl Future<Output = ()> + Send {
|
||||
// maybe respond with not implemented or something
|
||||
async{}
|
||||
async {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -242,23 +256,23 @@ async fn handle_incoming<T: RmcCallable + Send + Sync + 'static>(
|
|||
) {
|
||||
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 {
|
||||
error!("received too small rmc message.");
|
||||
error!("ending rmc gateway.");
|
||||
return
|
||||
return;
|
||||
};
|
||||
|
||||
// protocol 0 is hardcoded to be the no protocol protocol aka keepalive protocol
|
||||
if *proto_id == 0{
|
||||
if *proto_id == 0 {
|
||||
println!("got keepalive");
|
||||
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 {
|
||||
error!("ending rmc gateway.");
|
||||
return
|
||||
return;
|
||||
};
|
||||
|
||||
info!("got rmc response");
|
||||
|
|
@ -270,28 +284,34 @@ async fn handle_incoming<T: RmcCallable + Send + Sync + 'static>(
|
|||
} else {
|
||||
let Some(message) = RMCMessage::new(&mut Cursor::new(v)).display_err_or_some() else {
|
||||
error!("ending rmc gateway.");
|
||||
return
|
||||
return;
|
||||
};
|
||||
|
||||
let RMCMessage{
|
||||
let RMCMessage {
|
||||
protocol_id,
|
||||
method_id,
|
||||
call_id,
|
||||
rest_of_data
|
||||
rest_of_data,
|
||||
} = 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")
|
||||
}
|
||||
|
||||
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
|
||||
F: FnOnce(RmcConnection) -> Arc<T>,
|
||||
{
|
||||
|
|
@ -311,18 +331,12 @@ where
|
|||
{
|
||||
let exposed_object = exposed_object.clone();
|
||||
tokio::spawn(async move {
|
||||
handle_incoming(
|
||||
conn,
|
||||
exposed_object,
|
||||
notify,
|
||||
incoming
|
||||
).await;
|
||||
handle_incoming(conn, exposed_object, notify, incoming).await;
|
||||
});
|
||||
|
||||
|
||||
tokio::spawn(async move {
|
||||
while sending_conn.is_alive(){
|
||||
sending_conn.send([0,0,0,0,0].to_vec()).await;
|
||||
while sending_conn.is_alive() {
|
||||
sending_conn.send([0, 0, 0, 0, 0].to_vec()).await;
|
||||
sleep(Duration::from_secs(10)).await;
|
||||
}
|
||||
});
|
||||
|
|
@ -331,12 +345,20 @@ where
|
|||
exposed_object
|
||||
}
|
||||
|
||||
impl<T: RmcCallable> RmcCallable for Arc<T>{
|
||||
fn rmc_call(&self, responder: &SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec<u8>) -> impl Future<Output=()> + Send {
|
||||
self.as_ref().rmc_call(responder, protocol_id, method_id, call_id, rest)
|
||||
impl<T: RmcCallable> RmcCallable for Arc<T> {
|
||||
fn rmc_call(
|
||||
&self,
|
||||
responder: &SendingBufferConnection,
|
||||
protocol_id: u16,
|
||||
method_id: u32,
|
||||
call_id: u32,
|
||||
rest: Vec<u8>,
|
||||
) -> impl Future<Output = ()> + Send {
|
||||
self.as_ref()
|
||||
.rmc_call(responder, protocol_id, method_id, call_id, rest)
|
||||
}
|
||||
}
|
||||
|
||||
define_rmc_proto! {
|
||||
proto NoProto{}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
use macros::{method_id, rmc_proto};
|
||||
use crate::rmc::response::ErrorCode;
|
||||
use rnex_core::rmc::response::ErrorCode;
|
||||
|
||||
#[rmc_proto(3)]
|
||||
pub trait NatTraversal{
|
||||
33
rnex-core/src/rmc/protocols/ranking.rs
Normal file
33
rnex-core/src/rmc/protocols/ranking.rs
Normal 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>;
|
||||
}
|
||||
23
rnex-core/src/rmc/protocols/secure.rs
Normal file
23
rnex-core/src/rmc/protocols/secure.rs
Normal 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>;
|
||||
}
|
||||
|
|
@ -7,8 +7,8 @@ use std::io::{Read, Seek, Write};
|
|||
use std::mem::transmute;
|
||||
use bytemuck::bytes_of;
|
||||
use log::error;
|
||||
use v_byte_macros::EnumTryInto;
|
||||
use crate::endianness::{ReadExtensions, IS_BIG_ENDIAN};
|
||||
use v_byte_helpers::EnumTryInto;
|
||||
use v_byte_helpers::{ReadExtensions, IS_BIG_ENDIAN};
|
||||
use crate::rmc::response::ErrorCode::Core_Exception;
|
||||
use crate::rmc::structures::qresult::ERROR_MASK;
|
||||
use crate::util::SendingBufferConnection;
|
||||
|
|
@ -471,7 +471,7 @@ mod test {
|
|||
|
||||
#[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 rc4: Rc4<U5> =
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
use rnex_core::rmc::structures::{Result, RmcSerialize};
|
||||
use std::io::{Read, Write};
|
||||
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
use super::{Result, RmcSerialize};
|
||||
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Any{
|
||||
pub struct Any {
|
||||
pub name: String,
|
||||
pub data: Vec<u8>
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl RmcSerialize for Any{
|
||||
fn serialize(&self, writer: &mut dyn Write) -> Result<()> {
|
||||
impl RmcSerialize for Any {
|
||||
fn serialize(&self, writer: &mut impl Write) -> Result<()> {
|
||||
self.name.serialize(writer)?;
|
||||
|
||||
let u32_len = self.data.len() as u32;
|
||||
|
|
@ -21,7 +21,7 @@ impl RmcSerialize for Any{
|
|||
|
||||
Ok(())
|
||||
}
|
||||
fn deserialize(mut reader: &mut dyn Read) -> Result<Self> {
|
||||
fn deserialize(reader: &mut impl Read) -> Result<Self> {
|
||||
let name = String::deserialize(reader)?;
|
||||
|
||||
// also length ?
|
||||
|
|
@ -32,11 +32,6 @@ impl RmcSerialize for Any{
|
|||
|
||||
reader.read_exact(&mut data)?;
|
||||
|
||||
Ok(
|
||||
Any{
|
||||
name,
|
||||
data
|
||||
}
|
||||
)
|
||||
Ok(Any { name, data })
|
||||
}
|
||||
}
|
||||
}
|
||||
36
rnex-core/src/rmc/structures/buffer.rs
Normal file
36
rnex-core/src/rmc/structures/buffer.rs
Normal 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
Loading…
Add table
Add a link
Reference in a new issue