V0 #1
159 changed files with 8985 additions and 6571 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
|
.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
|
.idea
|
||||||
.env
|
.env
|
||||||
log
|
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:
|
variables:
|
||||||
IMAGE_NAME: "ci.perditum.com/perditum/rnex-splatoon"
|
DOCKER_TLS_CERTDIR: "/certs"
|
||||||
IMAGE_TAG: "${CI_COMMIT_REF_SLUG}"
|
IMAGE_TAG: "${CI_COMMIT_REF_SLUG}"
|
||||||
|
|
||||||
before_script:
|
|
||||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" ci.perditum.com
|
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- build-and-push
|
- build_and_test
|
||||||
|
|
||||||
build-and-push:
|
splatoon:
|
||||||
stage: build-and-push
|
stage: build_and_test
|
||||||
script:
|
script: ./.ci-scripts/make-edition.sh splatoon
|
||||||
- docker build -t "$IMAGE_NAME:$IMAGE_TAG" .
|
|
||||||
- docker tag "$IMAGE_NAME:$IMAGE_TAG" "$IMAGE_NAME:latest"
|
friends:
|
||||||
- docker push "$IMAGE_NAME:$IMAGE_TAG"
|
stage: build_and_test
|
||||||
- docker push "$IMAGE_NAME:latest"
|
script: ./.ci-scripts/make-edition.sh friends
|
||||||
|
|
|
||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -1,3 +0,0 @@
|
||||||
[submodule "grpc-protobufs"]
|
|
||||||
path = grpc-protobufs
|
|
||||||
url = https://github.com/PretendoNetwork/grpc-protobufs.git
|
|
||||||
2599
Cargo.lock
generated
2599
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
99
Cargo.toml
99
Cargo.toml
|
|
@ -1,91 +1,8 @@
|
||||||
[package]
|
[workspace]
|
||||||
name = "rust-nex"
|
resolver = "3"
|
||||||
version = "0.1.0"
|
members = [
|
||||||
edition = "2021"
|
"macros",
|
||||||
|
"rnex-core",
|
||||||
[profile.prod]
|
"prudpv1",
|
||||||
inherits = "release"
|
"prudpv0"
|
||||||
overflow-checks = false
|
, "proxy", "proxy-common", "prudplite"]
|
||||||
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"
|
|
||||||
|
|
|
||||||
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
|
WORKDIR /app
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN apk add --no-cache protobuf-dev git musl-dev lld openssl-dev openssl-libs-static
|
ARG EDITION
|
||||||
|
|
||||||
RUN git submodule update --init --recursive
|
RUN git submodule update --init --recursive
|
||||||
|
|
||||||
RUN OPENSSL_LIB_DIR=/usr/lib OPENSSL_INCLUDE_DIR=/usr/include/openssl OPENSSL_STATIC=1 RUSTFLAGS="-C relocation-model=static -C linker=ld.lld" cargo test --target x86_64-unknown-linux-musl
|
RUN ./test-edition.sh
|
||||||
RUN OPENSSL_LIB_DIR=/usr/lib OPENSSL_INCLUDE_DIR=/usr/include/openssl OPENSSL_STATIC=1 RUSTFLAGS="-C relocation-model=static -C linker=ld.lld" cargo build --profile prod --target x86_64-unknown-linux-musl
|
RUN ./build-edition.sh
|
||||||
|
|
||||||
FROM scratch AS final
|
FROM scratch AS node-holder
|
||||||
|
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/edge_node_holder_server /edge_node_holder_server
|
||||||
|
ENTRYPOINT ["/edge_node_holder_server"]
|
||||||
|
|
||||||
# Copy the compiled binary from the builder stage
|
|
||||||
COPY --from=builder /app/target/x86_64-unknown-linux-musl/prod/splatoon-server-rust /splatoon-server-rust
|
|
||||||
|
|
||||||
# Command to run the application
|
FROM scratch AS proxy-insecure
|
||||||
ENTRYPOINT ["/splatoon-server-rust"]
|
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/proxy_insecure /proxy_insecure
|
||||||
|
ENTRYPOINT ["/proxy_insecure"]
|
||||||
|
|
||||||
|
FROM scratch AS proxy-secure
|
||||||
|
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/proxy_secure /proxy_secure
|
||||||
|
ENTRYPOINT ["/proxy_secure"]
|
||||||
|
|
||||||
|
|
||||||
|
FROM scratch AS backend-auth
|
||||||
|
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/backend_server_insecure /backend_server_insecure
|
||||||
|
ENTRYPOINT ["/backend_server_insecure"]
|
||||||
|
|
||||||
|
FROM scratch AS backend-secure
|
||||||
|
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/backend_server_secure /backend_server_secure
|
||||||
|
ENTRYPOINT ["/backend_server_secure"]
|
||||||
|
|
||||||
|
# make sure the final output container is the dev container so that we can use it from the devcontainer.json
|
||||||
|
FROM build-container AS dev-container
|
||||||
|
RUN apk add openjdk21-jdk gcompat
|
||||||
|
|
|
||||||
15
README.md
15
README.md
|
|
@ -4,4 +4,17 @@
|
||||||
- Pretendo team for the rest of the Servers and Reverse engineering efforts
|
- Pretendo team for the rest of the Servers and Reverse engineering efforts
|
||||||
- Kinnay for his huge work on reversing nex servers and documentation(https://github.com/Kinnay/NintendoClients/)
|
- Kinnay for his huge work on reversing nex servers and documentation(https://github.com/Kinnay/NintendoClients/)
|
||||||
- Splatfestival testing team for helping us test our messes of code
|
- Splatfestival testing team for helping us test our messes of code
|
||||||
- The SPFN team(Andrea and DJMrTV)
|
- The SPFN team(Bloxer, Ceantix and RusticMaple)
|
||||||
|
|
||||||
|
This nex implementation was not created and is not intended to compete with pretendo,
|
||||||
|
we wholeheartedly support pretendo.
|
||||||
|
This project would never have been possible without their reverse engineering efforts.
|
||||||
|
As such if you want to respect the Authors wishes(Maple(Me) is pretty much the sole author of Rust-Nex), do not
|
||||||
|
use it if you mean any harm to pretendo. If you do show intent to harm them you will be blocked from ever contributing
|
||||||
|
and will be refused support, as such please also refrain from asking for support if you have been blocked by pretendo.
|
||||||
|
|
||||||
|
|
||||||
|
I felt like this needed to be said as there are far too many pretendo copycats who blatantly copy their code and use
|
||||||
|
their reversal efforts with no credits in sight in an attempt to harm them for some grudge or stupid reason.
|
||||||
|
I feel that by working together and not against each other we can reach a better and healthier future for the community,
|
||||||
|
health of developers and numerous more reasons.
|
||||||
|
|
|
||||||
13
build-edition.sh
Executable file
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"
|
||||||
29
editions.yaml
Normal file
29
editions.yaml
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
splatoon:
|
||||||
|
features:
|
||||||
|
- prudpv1
|
||||||
|
- v3-8-15
|
||||||
|
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
|
||||||
|
RNEX_ACCESS_KEY: "6f599f81"
|
||||||
|
splatoon2:
|
||||||
|
features:
|
||||||
|
- v4-3-11
|
||||||
|
- prudplite
|
||||||
|
- nx
|
||||||
|
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
|
||||||
|
RNEX_ACCESS_KEY: "ridfebb9"
|
||||||
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.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"r-efi",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.174"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "macros"
|
name = "macros"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
"rand",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.93"
|
version = "1.0.93"
|
||||||
|
|
@ -29,6 +69,41 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "5.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||||
|
dependencies = [
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.98"
|
version = "2.0.98"
|
||||||
|
|
@ -45,3 +120,41 @@ name = "unicode-ident"
|
||||||
version = "1.0.16"
|
version = "1.0.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.14.2+wasi-0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen-rt",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-rt"
|
||||||
|
version = "0.39.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.8.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,16 @@
|
||||||
[package]
|
[package]
|
||||||
name = "macros"
|
name = "macros"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
authors = ["DJMrTV <tvnebel@gmail.com>"]
|
authors = ["RusticMaple <tvnebel@gmail.com>"]
|
||||||
description = "A `cargo generate` template for quick-starting a procedural macro crate"
|
description = "A `cargo generate` template for quick-starting a procedural macro crate"
|
||||||
keywords = ["template", "proc_macro", "procmacro"]
|
keywords = ["template", "proc_macro", "procmacro"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
quote = "1.0.38"
|
quote = "1.0.38"
|
||||||
proc-macro2 = "1.0.93"
|
proc-macro2 = "1.0.93"
|
||||||
syn = { version = "2.0.98", features = ["full"] }
|
syn = { version = "2.0.98", features = ["full"] }
|
||||||
rand = "0.9.0"
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ use syn::parse::{Parse, ParseStream};
|
||||||
use syn::punctuated::Punctuated;
|
use syn::punctuated::Punctuated;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::{
|
use syn::{
|
||||||
parse_macro_input, Attribute, Data, DataStruct, DeriveInput, Fields, FnArg, LitInt, Pat, Token,
|
parse_macro_input, Attribute, Data, DataStruct, DeriveInput, Fields, FnArg, Lit, LitInt,
|
||||||
TraitItem,
|
LitStr, Pat, Token, TraitItem,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ProtoInputParams {
|
struct ProtoInputParams {
|
||||||
|
|
@ -48,7 +48,11 @@ impl Parse for ProtoInputParams {
|
||||||
fn gen_serialize_data_struct(
|
fn gen_serialize_data_struct(
|
||||||
s: DataStruct,
|
s: DataStruct,
|
||||||
struct_attr: Option<&Attribute>,
|
struct_attr: Option<&Attribute>,
|
||||||
) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
|
) -> (
|
||||||
|
proc_macro2::TokenStream,
|
||||||
|
proc_macro2::TokenStream,
|
||||||
|
proc_macro2::TokenStream,
|
||||||
|
) {
|
||||||
let serialize_base_content = {
|
let serialize_base_content = {
|
||||||
let mut serialize_content = quote! {};
|
let mut serialize_content = quote! {};
|
||||||
|
|
||||||
|
|
@ -65,7 +69,7 @@ fn gen_serialize_data_struct(
|
||||||
let ident = f.ident.as_ref().unwrap();
|
let ident = f.ident.as_ref().unwrap();
|
||||||
|
|
||||||
serialize_content.append_all(quote! {
|
serialize_content.append_all(quote! {
|
||||||
self.#ident.serialize(writer)?;
|
rnex_core::rmc::structures::RmcSerialize::serialize(&self.#ident, writer)?;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,6 +123,25 @@ fn gen_serialize_data_struct(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let write_size = {
|
||||||
|
let mut size_content = quote! { 0 };
|
||||||
|
|
||||||
|
for f in &s.fields {
|
||||||
|
let ident = f.ident.as_ref().unwrap();
|
||||||
|
|
||||||
|
size_content.append_all(quote! {
|
||||||
|
+ rnex_core::rmc::structures::RmcSerialize::serialize_write_size(&self.#ident)?
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
size_content
|
||||||
|
};
|
||||||
|
let write_size = if let Some(_) = struct_attr {
|
||||||
|
quote! { #write_size + if rnex_core::config::FEATURE_HAS_STRUCT_HEADER{ 5 } else { 0 } }
|
||||||
|
} else {
|
||||||
|
write_size
|
||||||
|
};
|
||||||
|
|
||||||
// generate base with extends stuff
|
// generate base with extends stuff
|
||||||
|
|
||||||
let serialize_base_content = if let Some(attr) = struct_attr {
|
let serialize_base_content = if let Some(attr) = struct_attr {
|
||||||
|
|
@ -143,9 +166,18 @@ fn gen_serialize_data_struct(
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#pre_inner
|
#pre_inner
|
||||||
rust_nex::rmc::structures::rmc_struct::write_struct(writer, #version, |mut writer|{
|
rnex_core::rmc::structures::rmc_struct::write_struct(
|
||||||
#serialize_base_content
|
writer,
|
||||||
})?;
|
#version,
|
||||||
|
rnex_core::rmc::structures::helpers::len_of_write(
|
||||||
|
|writer|{
|
||||||
|
#serialize_base_content
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|writer|{
|
||||||
|
#serialize_base_content
|
||||||
|
}
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -176,7 +208,7 @@ fn gen_serialize_data_struct(
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#pre_inner
|
#pre_inner
|
||||||
Ok(rust_nex::rmc::structures::rmc_struct::read_struct(reader, #version, move |mut reader|{
|
Ok(rnex_core::rmc::structures::rmc_struct::read_struct(reader, #version, move |mut reader|{
|
||||||
#deserialize_base_content
|
#deserialize_base_content
|
||||||
})?)
|
})?)
|
||||||
}
|
}
|
||||||
|
|
@ -184,7 +216,13 @@ fn gen_serialize_data_struct(
|
||||||
deserialize_base_content
|
deserialize_base_content
|
||||||
};
|
};
|
||||||
|
|
||||||
(serialize_base_content, deserialize_base_content)
|
let write_size = quote! {
|
||||||
|
fn serialize_write_size(&self) -> rnex_core::rmc::structures::Result<u32>{
|
||||||
|
Ok(#write_size)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(serialize_base_content, deserialize_base_content, write_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_derive(RmcSerialize, attributes(extends, rmc_struct))]
|
#[proc_macro_derive(RmcSerialize, attributes(extends, rmc_struct))]
|
||||||
|
|
@ -210,7 +248,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
|
||||||
panic!("rmc struct type MUST be a struct");
|
panic!("rmc struct type MUST be a struct");
|
||||||
};*/
|
};*/
|
||||||
|
|
||||||
let (serialize_base_content, deserialize_base_content) = match derive_input.data {
|
let (serialize_base_content, deserialize_base_content, write_size) = match derive_input.data {
|
||||||
Data::Struct(s) => gen_serialize_data_struct(s, struct_attr),
|
Data::Struct(s) => gen_serialize_data_struct(s, struct_attr),
|
||||||
Data::Enum(e) => {
|
Data::Enum(e) => {
|
||||||
let Some(repr_attr) = repr_attr else {
|
let Some(repr_attr) = repr_attr else {
|
||||||
|
|
@ -221,6 +259,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
let mut inner_match_de = quote! {};
|
let mut inner_match_de = quote! {};
|
||||||
let mut inner_match_se = quote! {};
|
let mut inner_match_se = quote! {};
|
||||||
|
//let mut inner_match_len = quote!{};
|
||||||
|
|
||||||
for variant in e.variants {
|
for variant in e.variants {
|
||||||
let Some((_, val)) = variant.discriminant else {
|
let Some((_, val)) = variant.discriminant else {
|
||||||
|
|
@ -235,7 +274,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
|
||||||
let name = &field.ident;
|
let name = &field.ident;
|
||||||
|
|
||||||
base.append_all(quote!{
|
base.append_all(quote!{
|
||||||
#name: <#ty as rust_nex::rmc::structures::RmcSerialize>::deserialize(reader)?,
|
#name: <#ty as rnex_core::rmc::structures::RmcSerialize>::deserialize(reader)?,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -248,7 +287,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
|
||||||
let ty = &field.ty;
|
let ty = &field.ty;
|
||||||
|
|
||||||
base.append_all(quote!{
|
base.append_all(quote!{
|
||||||
<#ty as rust_nex::rmc::structures::RmcSerialize>::deserialize(reader)?,
|
<#ty as rnex_core::rmc::structures::RmcSerialize>::deserialize(reader)?,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -260,7 +299,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut se_with_fields = quote! {
|
let mut se_with_fields = quote! {
|
||||||
<#ty as rust_nex::rmc::structures::RmcSerialize>::serialize(&#val, writer)?;
|
<#ty as rnex_core::rmc::structures::RmcSerialize>::serialize(&#val, writer)?;
|
||||||
};
|
};
|
||||||
|
|
||||||
match &variant.fields {
|
match &variant.fields {
|
||||||
|
|
@ -270,7 +309,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
|
||||||
let name = &field.ident;
|
let name = &field.ident;
|
||||||
|
|
||||||
se_with_fields.append_all(quote!{
|
se_with_fields.append_all(quote!{
|
||||||
<#ty as rust_nex::rmc::structures::RmcSerialize>::serialize(#name ,writer)?;
|
<#ty as rnex_core::rmc::structures::RmcSerialize>::serialize(#name ,writer)?;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -281,7 +320,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
|
||||||
let ident = Ident::new(&format!("val_{}", i), Span::call_site());
|
let ident = Ident::new(&format!("val_{}", i), Span::call_site());
|
||||||
|
|
||||||
se_with_fields.append_all(quote!{
|
se_with_fields.append_all(quote!{
|
||||||
<#ty as rust_nex::rmc::structures::RmcSerialize>::serialize(#ident, writer)?;
|
<#ty as rnex_core::rmc::structures::RmcSerialize>::serialize(#ident, writer)?;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -344,15 +383,15 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
|
||||||
};
|
};
|
||||||
|
|
||||||
let deserialize_base_content = quote! {
|
let deserialize_base_content = quote! {
|
||||||
let val: Self = match <#ty as rust_nex::rmc::structures::RmcSerialize>::deserialize(reader)?{
|
let val: Self = match <#ty as rnex_core::rmc::structures::RmcSerialize>::deserialize(reader)?{
|
||||||
#inner_match_de
|
#inner_match_de
|
||||||
v => return Err(rust_nex::rmc::structures::Error::UnexpectedValue(v as _))
|
v => return Err(rnex_core::rmc::structures::Error::UnexpectedValue(v as _))
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(val)
|
Ok(val)
|
||||||
};
|
};
|
||||||
|
|
||||||
(serialize_base_content, deserialize_base_content)
|
(serialize_base_content, deserialize_base_content, quote! {})
|
||||||
}
|
}
|
||||||
Data::Union(_) => {
|
Data::Union(_) => {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
|
|
@ -361,18 +400,27 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
// generate base data
|
// generate base data
|
||||||
|
|
||||||
|
let str_name = Lit::Str(LitStr::new(
|
||||||
|
&derive_input.ident.to_string(),
|
||||||
|
derive_input.ident.span(),
|
||||||
|
));
|
||||||
let ident = derive_input.ident;
|
let ident = derive_input.ident;
|
||||||
|
|
||||||
let tokens = quote! {
|
let tokens = quote! {
|
||||||
impl rust_nex::rmc::structures::RmcSerialize for #ident{
|
impl rnex_core::rmc::structures::RmcSerialize for #ident{
|
||||||
fn serialize(&self, writer: &mut dyn ::std::io::Write) -> rust_nex::rmc::structures::Result<()>{
|
#[inline(always)]
|
||||||
|
fn serialize(&self, writer: &mut impl ::std::io::Write) -> rnex_core::rmc::structures::Result<()>{
|
||||||
#serialize_base_content
|
#serialize_base_content
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn deserialize(reader: &mut impl ::std::io::Read) -> rnex_core::rmc::structures::Result<Self>{
|
||||||
|
#deserialize_base_content
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize(reader: &mut dyn ::std::io::Read) -> rust_nex::rmc::structures::Result<Self>{
|
#write_size
|
||||||
#deserialize_base_content
|
|
||||||
|
fn name() -> &'static str{
|
||||||
|
#str_name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -511,8 +559,8 @@ pub fn rmc_struct(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl rust_nex::rmc::protocols::RmcCallable for #struct_name{
|
impl rnex_core::rmc::protocols::RmcCallable for #struct_name{
|
||||||
async fn rmc_call(&self, remote_response_connection: &rust_nex::util::SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec<u8>){
|
async fn rmc_call(&self, remote_response_connection: &rnex_core::util::SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec<u8>){
|
||||||
<Self as #ident>::rmc_call(self, remote_response_connection, protocol_id, method_id, call_id, rest).await;
|
<Self as #ident>::rmc_call(self, remote_response_connection, protocol_id, method_id, call_id, rest).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,46 @@
|
||||||
use proc_macro2::{Ident, Span, TokenStream};
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
use syn::{LitInt, LitStr, ReturnType, Type};
|
|
||||||
use syn::token::{Brace, Paren, Semi};
|
use syn::token::{Brace, Paren, Semi};
|
||||||
|
use syn::{Attribute, LitInt, LitStr, ReturnType, Type};
|
||||||
|
|
||||||
pub struct ProtoMethodData{
|
pub struct ProtoMethodData {
|
||||||
pub id: LitInt,
|
pub id: LitInt,
|
||||||
pub name: Ident,
|
pub name: Ident,
|
||||||
pub parameters: Vec<(Ident, Type)>,
|
pub parameters: Vec<(Ident, Type)>,
|
||||||
pub ret_val: ReturnType,
|
pub ret_val: ReturnType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// This is a representation of the code generated by `rmc_proto` it serves to split the logic of
|
/// This is a representation of the code generated by `rmc_proto` it serves to split the logic of
|
||||||
/// acquiring data from the actual generation to tidy up the process into first getting then
|
/// acquiring data from the actual generation to tidy up the process into first getting then
|
||||||
/// generating.
|
/// generating.
|
||||||
///
|
///
|
||||||
/// Use the [`ToTokens`] trait to generate the actual code.
|
/// Use the [`ToTokens`] trait to generate the actual code.
|
||||||
pub struct RmcProtocolData{
|
pub struct RmcProtocolData {
|
||||||
pub has_returns: bool,
|
pub has_returns: bool,
|
||||||
pub id: LitInt,
|
pub id: LitInt,
|
||||||
pub name: Ident,
|
pub name: Ident,
|
||||||
pub methods: Vec<ProtoMethodData>
|
pub methods: Vec<ProtoMethodData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RmcProtocolData{
|
impl RmcProtocolData {
|
||||||
fn generate_raw_trait(&self, tokens: &mut TokenStream){
|
fn generate_raw_trait(&self, tokens: &mut TokenStream) {
|
||||||
let Self{
|
let Self {
|
||||||
has_returns,
|
has_returns,
|
||||||
name,
|
name,
|
||||||
id,
|
id,
|
||||||
methods
|
methods,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
// this gives us the name which the identifier of the corresponding Raw trait
|
// this gives us the name which the identifier of the corresponding Raw trait
|
||||||
let raw_name = Ident::new(&format!("Raw{}", name), name.span());
|
let raw_name = Ident::new(&format!("Raw{}", name), name.span());
|
||||||
|
|
||||||
|
|
||||||
// boilerplate tokens which all raw traits need
|
// boilerplate tokens which all raw traits need
|
||||||
quote!{
|
quote! {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
#[allow(unused_must_use)]
|
||||||
pub trait #raw_name: #name
|
pub trait #raw_name: #name
|
||||||
}.to_tokens(tokens);
|
}
|
||||||
|
.to_tokens(tokens);
|
||||||
|
|
||||||
// generate the body of the raw protocol trait
|
// generate the body of the raw protocol trait
|
||||||
Brace::default().surround(tokens, |tokens|{
|
Brace::default().surround(tokens, |tokens|{
|
||||||
|
|
@ -54,6 +54,7 @@ impl RmcProtocolData{
|
||||||
|
|
||||||
let raw_name = Ident::new(&format!("raw_{}", name), name.span());
|
let raw_name = Ident::new(&format!("raw_{}", name), name.span());
|
||||||
quote!{
|
quote!{
|
||||||
|
#[inline(always)]
|
||||||
async fn #raw_name
|
async fn #raw_name
|
||||||
}.to_tokens(tokens);
|
}.to_tokens(tokens);
|
||||||
|
|
||||||
|
|
@ -73,7 +74,7 @@ impl RmcProtocolData{
|
||||||
for (param_name, param_type) in parameters{
|
for (param_name, param_type) in parameters{
|
||||||
quote!{
|
quote!{
|
||||||
let Ok(#param_name) =
|
let Ok(#param_name) =
|
||||||
<#param_type as rust_nex::rmc::structures::RmcSerialize>::deserialize(
|
<#param_type as rnex_core::rmc::structures::RmcSerialize>::deserialize(
|
||||||
&mut cursor
|
&mut cursor
|
||||||
) else
|
) else
|
||||||
}.to_tokens(tokens);
|
}.to_tokens(tokens);
|
||||||
|
|
@ -84,7 +85,7 @@ impl RmcProtocolData{
|
||||||
quote! {
|
quote! {
|
||||||
{
|
{
|
||||||
log::error!(#error_msg);
|
log::error!(#error_msg);
|
||||||
return Err(rust_nex::rmc::response::ErrorCode::Core_InvalidArgument);
|
return Err(rnex_core::rmc::response::ErrorCode::Core_InvalidArgument);
|
||||||
};
|
};
|
||||||
}.to_tokens(tokens)
|
}.to_tokens(tokens)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -116,7 +117,7 @@ impl RmcProtocolData{
|
||||||
quote!{
|
quote!{
|
||||||
let retval = retval?;
|
let retval = retval?;
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
rust_nex::rmc::structures::RmcSerialize::serialize(&retval, &mut vec).ok();
|
rnex_core::rmc::structures::RmcSerialize::serialize(&retval, &mut vec).ok();
|
||||||
Ok(vec)
|
Ok(vec)
|
||||||
}.to_tokens(tokens);
|
}.to_tokens(tokens);
|
||||||
}
|
}
|
||||||
|
|
@ -124,9 +125,10 @@ impl RmcProtocolData{
|
||||||
}
|
}
|
||||||
|
|
||||||
quote!{
|
quote!{
|
||||||
|
#[inline(always)]
|
||||||
async fn rmc_call_proto(
|
async fn rmc_call_proto(
|
||||||
&self,
|
&self,
|
||||||
remote_response_connection: &rust_nex::util::SendingBufferConnection,
|
remote_response_connection: &rnex_core::util::SendingBufferConnection,
|
||||||
method_id: u32,
|
method_id: u32,
|
||||||
call_id: u32,
|
call_id: u32,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
|
|
@ -165,7 +167,7 @@ impl RmcProtocolData{
|
||||||
}.to_tokens(tokens);
|
}.to_tokens(tokens);
|
||||||
if self.has_returns {
|
if self.has_returns {
|
||||||
quote! {
|
quote! {
|
||||||
Err(rust_nex::rmc::response::ErrorCode::Core_NotImplemented)
|
Err(rnex_core::rmc::response::ErrorCode::Core_NotImplemented)
|
||||||
}.to_tokens(tokens);
|
}.to_tokens(tokens);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -176,7 +178,7 @@ impl RmcProtocolData{
|
||||||
|
|
||||||
if *has_returns{
|
if *has_returns{
|
||||||
quote!{
|
quote!{
|
||||||
rust_nex::rmc::response::send_result(
|
rnex_core::rmc::response::send_result(
|
||||||
remote_response_connection,
|
remote_response_connection,
|
||||||
ret,
|
ret,
|
||||||
#id,
|
#id,
|
||||||
|
|
@ -188,9 +190,10 @@ impl RmcProtocolData{
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
quote!{
|
quote! {
|
||||||
impl<T: #name> #raw_name for T{}
|
impl<T: #name> #raw_name for T{}
|
||||||
}.to_tokens(tokens);
|
}
|
||||||
|
.to_tokens(tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_raw_remote_trait(&self, tokens: &mut TokenStream) {
|
fn generate_raw_remote_trait(&self, tokens: &mut TokenStream) {
|
||||||
|
|
@ -205,12 +208,13 @@ impl RmcProtocolData{
|
||||||
// this gives us the name which the identifier of the corresponding Raw trait
|
// this gives us the name which the identifier of the corresponding Raw trait
|
||||||
let remote_name = Ident::new(&format!("Remote{}", name), name.span());
|
let remote_name = Ident::new(&format!("Remote{}", name), name.span());
|
||||||
|
|
||||||
|
|
||||||
// boilerplate tokens which all raw traits need
|
// boilerplate tokens which all raw traits need
|
||||||
quote!{
|
quote! {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub trait #remote_name: rust_nex::rmc::protocols::HasRmcConnection
|
#[allow(unused_must_use)]
|
||||||
}.to_tokens(tokens);
|
pub trait #remote_name: rnex_core::rmc::protocols::HasRmcConnection
|
||||||
|
}
|
||||||
|
.to_tokens(tokens);
|
||||||
|
|
||||||
// generate the body of the raw protocol trait
|
// generate the body of the raw protocol trait
|
||||||
Brace::default().surround(tokens, |tokens|{
|
Brace::default().surround(tokens, |tokens|{
|
||||||
|
|
@ -247,12 +251,12 @@ impl RmcProtocolData{
|
||||||
|
|
||||||
for (param_name, param_type) in parameters{
|
for (param_name, param_type) in parameters{
|
||||||
quote!{
|
quote!{
|
||||||
rust_nex::result::ResultExtension::display_err_or_some(
|
rnex_core::result::ResultExtension::display_err_or_some(
|
||||||
<#param_type as rust_nex::rmc::structures::RmcSerialize>::serialize(
|
<#param_type as rnex_core::rmc::structures::RmcSerialize>::serialize(
|
||||||
&#param_name,
|
&#param_name,
|
||||||
&mut cursor
|
&mut cursor
|
||||||
)
|
)
|
||||||
).ok_or(rust_nex::rmc::response::ErrorCode::Core_InvalidArgument)
|
).ok_or(rnex_core::rmc::response::ErrorCode::Core_InvalidArgument)
|
||||||
}.to_tokens(tokens);
|
}.to_tokens(tokens);
|
||||||
if self.has_returns {
|
if self.has_returns {
|
||||||
quote! {
|
quote! {
|
||||||
|
|
@ -268,25 +272,25 @@ impl RmcProtocolData{
|
||||||
quote!{
|
quote!{
|
||||||
let call_id = rand::random();
|
let call_id = rand::random();
|
||||||
|
|
||||||
let message = rust_nex::rmc::message::RMCMessage{
|
let message = rnex_core::rmc::message::RMCMessage{
|
||||||
call_id,
|
call_id,
|
||||||
method_id: #method_id,
|
method_id: #method_id,
|
||||||
protocol_id: #proto_id,
|
protocol_id: #proto_id,
|
||||||
rest_of_data: send_data
|
rest_of_data: send_data
|
||||||
};
|
};
|
||||||
|
|
||||||
let rmc_conn = <Self as rust_nex::rmc::protocols::HasRmcConnection>::get_connection(self);
|
let rmc_conn = <Self as rnex_core::rmc::protocols::HasRmcConnection>::get_connection(self);
|
||||||
}.to_tokens(tokens);
|
}.to_tokens(tokens);
|
||||||
|
|
||||||
if *has_returns{
|
if *has_returns{
|
||||||
quote!{
|
quote!{
|
||||||
rust_nex::result::ResultExtension::display_err_or_some(
|
rnex_core::result::ResultExtension::display_err_or_some(
|
||||||
rmc_conn.make_raw_call(&message).await
|
rmc_conn.make_raw_call(&message).await
|
||||||
).ok_or(rust_nex::rmc::response::ErrorCode::Core_Exception)
|
).ok_or(rnex_core::rmc::response::ErrorCode::Core_Exception)
|
||||||
}.to_tokens(tokens);
|
}.to_tokens(tokens);
|
||||||
} else {
|
} else {
|
||||||
quote!{
|
quote!{
|
||||||
rust_nex::result::ResultExtension::display_err_or_some(
|
rnex_core::result::ResultExtension::display_err_or_some(
|
||||||
rmc_conn.make_raw_call_no_response(&message).await
|
rmc_conn.make_raw_call_no_response(&message).await
|
||||||
);
|
);
|
||||||
}.to_tokens(tokens);
|
}.to_tokens(tokens);
|
||||||
|
|
@ -295,40 +299,29 @@ impl RmcProtocolData{
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_raw_info(&self, tokens: &mut TokenStream){
|
fn generate_raw_info(&self, tokens: &mut TokenStream) {
|
||||||
let Self{
|
let Self { name, id, .. } = self;
|
||||||
name,
|
|
||||||
id,
|
|
||||||
..
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
let raw_info_name = Ident::new(&format!("Raw{}Info", name), Span::call_site());
|
let raw_info_name = Ident::new(&format!("Raw{}Info", name), Span::call_site());
|
||||||
|
|
||||||
|
quote! {
|
||||||
quote!{
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct #raw_info_name;
|
pub struct #raw_info_name;
|
||||||
|
|
||||||
impl #raw_info_name {
|
impl #raw_info_name {
|
||||||
pub const PROTOCOL_ID: u16 = #id;
|
pub const PROTOCOL_ID: u16 = #id;
|
||||||
}
|
}
|
||||||
}.to_tokens(tokens);
|
}
|
||||||
|
.to_tokens(tokens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToTokens for RmcProtocolData{
|
impl ToTokens for RmcProtocolData {
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
self.generate_raw_trait(tokens);
|
self.generate_raw_trait(tokens);
|
||||||
self.generate_raw_info(tokens);
|
self.generate_raw_info(tokens);
|
||||||
self.generate_raw_remote_trait(tokens);
|
self.generate_raw_remote_trait(tokens);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
BIN
out/backend_server_secure
Executable file
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.
11
proxy-common/Cargo.toml
Normal file
11
proxy-common/Cargo.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[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"
|
||||||
|
hex = "0.4.3"
|
||||||
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::{
|
||||||
|
PID,
|
||||||
|
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"),
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const RNEX_ACCESS_KEY: &'static str = env!("RNEX_ACCESS_KEY");
|
||||||
|
|
||||||
|
#[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, self_private.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: PID,
|
||||||
|
) -> 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let data = ConnectionInitData {
|
||||||
|
prudpsock_addr: addr,
|
||||||
|
pid: pid,
|
||||||
|
}
|
||||||
|
.to_data()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if let Err(e) = stream.send_buffer(&data).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);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
proxy/Cargo.toml
Normal file
30
proxy/Cargo.toml
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
[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 }
|
||||||
|
prudplite = { path = "../prudplite", 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"]
|
||||||
|
prudplite = ["dep:prudplite"]
|
||||||
|
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;
|
||||||
|
}
|
||||||
21
proxy/src/lib.rs
Normal file
21
proxy/src/lib.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
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 if #[cfg(feature = "prudplite")]{
|
||||||
|
pub use prudplite::*;
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
18
prudplite/Cargo.toml
Normal file
18
prudplite/Cargo.toml
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
[package]
|
||||||
|
name = "prudplite"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
nx = []
|
||||||
|
v4-3-11 = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rnex-core = { path = "../rnex-core", version = "0.1.1" }
|
||||||
|
tokio = { version = "1.47.0", features = ["full"] }
|
||||||
|
bytemuck = { version = "1.23.1", features = ["derive"] }
|
||||||
|
proxy-common = {path = "../proxy-common"}
|
||||||
|
tokio-tungstenite = {version = "0.28.0", features = ["rustls", "rustls-tls-native-roots"]}
|
||||||
|
log = "0.4.25"
|
||||||
|
futures-util = "0.3.31"
|
||||||
|
v-byte-helpers = { git = "https://github.com/RusticMaple/VByteMacros", version = "0.1.1" }
|
||||||
14
prudplite/src/crypto/insecure.rs
Normal file
14
prudplite/src/crypto/insecure.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
use rnex_core::PID;
|
||||||
|
|
||||||
|
use crate::crypto::Crypto;
|
||||||
|
|
||||||
|
pub struct Insecure;
|
||||||
|
|
||||||
|
impl Crypto for Insecure {
|
||||||
|
fn new_connection(&self, data: &[u8]) -> Option<(PID, Vec<u8>)> {
|
||||||
|
Some((100, vec![]))
|
||||||
|
}
|
||||||
|
fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
}
|
||||||
9
prudplite/src/crypto/mod.rs
Normal file
9
prudplite/src/crypto/mod.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
use rnex_core::PID;
|
||||||
|
|
||||||
|
pub mod insecure;
|
||||||
|
pub mod secure;
|
||||||
|
|
||||||
|
pub trait Crypto: 'static + Send + Sync {
|
||||||
|
fn new_connection(&self, data: &[u8]) -> Option<(PID, Vec<u8>)>;
|
||||||
|
fn new() -> Self;
|
||||||
|
}
|
||||||
27
prudplite/src/crypto/secure.rs
Normal file
27
prudplite/src/crypto/secure.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
use rnex_core::{
|
||||||
|
PID, executables::common::SECURE_SERVER_ACCOUNT, nex::account::Account,
|
||||||
|
prudp::ticket::read_secure_connection_data, rmc::structures::RmcSerialize,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::crypto::Crypto;
|
||||||
|
|
||||||
|
pub struct Secure(&'static Account);
|
||||||
|
|
||||||
|
impl Crypto for Secure {
|
||||||
|
fn new_connection(&self, data: &[u8]) -> Option<(PID, Vec<u8>)> {
|
||||||
|
let (_, pid, check_value) = read_secure_connection_data(data, &self.0)?;
|
||||||
|
|
||||||
|
let check_value_response = check_value + 1;
|
||||||
|
|
||||||
|
let data = bytemuck::bytes_of(&check_value_response);
|
||||||
|
|
||||||
|
let mut response = Vec::new();
|
||||||
|
|
||||||
|
data.serialize(&mut response).ok()?;
|
||||||
|
|
||||||
|
Some((pid, response))
|
||||||
|
}
|
||||||
|
fn new() -> Self {
|
||||||
|
Self(&SECURE_SERVER_ACCOUNT)
|
||||||
|
}
|
||||||
|
}
|
||||||
309
prudplite/src/lib.rs
Normal file
309
prudplite/src/lib.rs
Normal file
|
|
@ -0,0 +1,309 @@
|
||||||
|
pub mod crypto;
|
||||||
|
mod packet;
|
||||||
|
|
||||||
|
use std::{collections::HashMap, net::SocketAddr, sync::Arc};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
crypto::{Crypto, insecure::Insecure, secure::Secure},
|
||||||
|
packet::{LiteHeader, LitePacket, PacketSpecificData, StreamTypes, create_packet_from},
|
||||||
|
};
|
||||||
|
use futures_util::{SinkExt, StreamExt};
|
||||||
|
use log::{error, info, warn};
|
||||||
|
use proxy_common::{ProxyStartupParam, new_backend_connection};
|
||||||
|
use rnex_core::{
|
||||||
|
PID,
|
||||||
|
prudp::{
|
||||||
|
socket_addr::PRUDPSockAddr,
|
||||||
|
types_flags::{
|
||||||
|
TypesFlags,
|
||||||
|
flags::{ACK, NEED_ACK, RELIABLE},
|
||||||
|
types::{CONNECT, DATA, DISCONNECT, SYN},
|
||||||
|
},
|
||||||
|
virtual_port::VirtualPort,
|
||||||
|
},
|
||||||
|
util::SplittableBufferConnection,
|
||||||
|
};
|
||||||
|
use tokio::net::{TcpListener, TcpStream};
|
||||||
|
use tokio_tungstenite::{
|
||||||
|
WebSocketStream,
|
||||||
|
tungstenite::{
|
||||||
|
Bytes, Message, client::IntoClientRequest, http::header::ACCESS_CONTROL_REQUEST_METHOD,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConnectionState {
|
||||||
|
param: Arc<ProxyStartupParam>,
|
||||||
|
active: bool,
|
||||||
|
websocket: WebSocketStream<TcpStream>,
|
||||||
|
pid: PID,
|
||||||
|
backend_conn: SplittableBufferConnection,
|
||||||
|
addr: PRUDPSockAddr,
|
||||||
|
incoming_reliable: HashMap<u16, LitePacket<Bytes>>,
|
||||||
|
client_reliable_counter: u16,
|
||||||
|
server_reliable_counter: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConnectionState {
|
||||||
|
pub async fn handle_incoming_prudp(&mut self, packet: LitePacket<Bytes>, sorted: bool) {
|
||||||
|
let Some(header) = packet.header() else {
|
||||||
|
warn!("invalid data on connection");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (header.types_flags.get_flags() & NEED_ACK) != 0 {
|
||||||
|
let data = create_packet_from(
|
||||||
|
LiteHeader {
|
||||||
|
stream_types: StreamTypes::new(
|
||||||
|
self.param.virtual_port.get_stream_type(),
|
||||||
|
self.addr.virtual_port.get_stream_type(),
|
||||||
|
),
|
||||||
|
source_port: self.param.virtual_port.get_port_number(),
|
||||||
|
destination_port: self.addr.virtual_port.get_port_number(),
|
||||||
|
fragment_id: header.fragment_id,
|
||||||
|
types_flags: TypesFlags::default()
|
||||||
|
.types(header.types_flags.get_types())
|
||||||
|
.flags(ACK),
|
||||||
|
sequence_id: header.sequence_id,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
&[],
|
||||||
|
&[],
|
||||||
|
);
|
||||||
|
let data: Bytes = data.into();
|
||||||
|
if header.types_flags.get_types() == DISCONNECT {
|
||||||
|
self.websocket.send(Message::Binary(data.clone())).await;
|
||||||
|
self.websocket.send(Message::Binary(data.clone())).await;
|
||||||
|
}
|
||||||
|
self.websocket.send(Message::Binary(data)).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.types_flags.get_flags() & ACK) != 0 {
|
||||||
|
// we can just safely ignore acks, we ARE sending over tcp after all already guarantees that our packets will arrive
|
||||||
|
// we can however not guarantee the order of incoming client packets so we should still take care of that
|
||||||
|
// (the client might be doing some funny things which we dont know of)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.types_flags.get_flags() & RELIABLE != 0) & !sorted {
|
||||||
|
self.incoming_reliable.insert(header.sequence_id, packet);
|
||||||
|
if self.incoming_reliable.len() > 5 {
|
||||||
|
self.active = false;
|
||||||
|
warn!("client is spamming out of order reliable packets, throwing out");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match header.types_flags.get_types() {
|
||||||
|
DATA => {
|
||||||
|
if header.fragment_id != 0 {
|
||||||
|
warn!("fragmented packets arent yet supported");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(payload) = packet.payload() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
self.backend_conn.send(payload.into()).await;
|
||||||
|
}
|
||||||
|
PING => {}
|
||||||
|
v => {
|
||||||
|
info!("unimplemented packet type: {}", v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub async fn process_reliable(&mut self) {
|
||||||
|
while let Some(v) = self.incoming_reliable.remove(&self.client_reliable_counter) {
|
||||||
|
self.handle_incoming_prudp(v, true).await;
|
||||||
|
self.client_reliable_counter += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub async fn handle_connection(&mut self) {
|
||||||
|
while self.active {
|
||||||
|
tokio::select! {
|
||||||
|
v = self.websocket.next() => {
|
||||||
|
match v {
|
||||||
|
Some(Ok(Message::Binary(v))) => {
|
||||||
|
self.handle_incoming_prudp(LitePacket::new(v), false).await;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
info!("client disconnected or errored out");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v = self.backend_conn.recv() => {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn websocket_thread_unconnected<C: Crypto>(
|
||||||
|
param: Arc<ProxyStartupParam>,
|
||||||
|
crypto: Arc<C>,
|
||||||
|
conn: TcpStream,
|
||||||
|
addr: SocketAddr,
|
||||||
|
) {
|
||||||
|
let mut websocket = match tokio_tungstenite::accept_async(conn).await {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
error!("error accepting websocket connection: {}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
while let Some(Ok(v)) = websocket.next().await {
|
||||||
|
match v {
|
||||||
|
Message::Binary(b) => {
|
||||||
|
let packet = LitePacket::new(b);
|
||||||
|
|
||||||
|
let Some(header) = packet.header() else {
|
||||||
|
error!("got malformed message, disconnecting");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
match header.types_flags.get_types() {
|
||||||
|
SYN => {
|
||||||
|
let Some(supported) = packet.packet_specific_iter() else {
|
||||||
|
error!("got malformed message, disconnecting");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(PacketSpecificData::SupportedFunctions(s)) = supported
|
||||||
|
.into_iter()
|
||||||
|
.find(|v| matches!(v, PacketSpecificData::SupportedFunctions(_)))
|
||||||
|
else {
|
||||||
|
error!("got malformed message, disconnecting");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let data = create_packet_from(
|
||||||
|
LiteHeader {
|
||||||
|
destination_port: header.source_port,
|
||||||
|
source_port: param.virtual_port.get_port_number(),
|
||||||
|
stream_types: StreamTypes::new(
|
||||||
|
param.virtual_port.get_stream_type(),
|
||||||
|
header.stream_types.source(),
|
||||||
|
),
|
||||||
|
fragment_id: 0,
|
||||||
|
sequence_id: 0,
|
||||||
|
types_flags: TypesFlags::default().types(SYN).flags(ACK),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
&[
|
||||||
|
PacketSpecificData::SupportedFunctions(s & 0xFF),
|
||||||
|
PacketSpecificData::ConnectionSignature([0; 16]),
|
||||||
|
],
|
||||||
|
&[],
|
||||||
|
);
|
||||||
|
websocket.send(Message::Binary(data.into())).await;
|
||||||
|
}
|
||||||
|
CONNECT => {
|
||||||
|
let Some(supported) = packet.packet_specific_iter() else {
|
||||||
|
error!("got malformed message, disconnecting");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(PacketSpecificData::SupportedFunctions(s)) = supported
|
||||||
|
.into_iter()
|
||||||
|
.find(|v| matches!(v, PacketSpecificData::SupportedFunctions(_)))
|
||||||
|
else {
|
||||||
|
error!("got malformed message, disconnecting");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(data) = packet.payload() else {
|
||||||
|
error!("got malformed message, disconnecting");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some((pid, data)) = crypto.new_connection(data) else {
|
||||||
|
error!("invalid login data");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let data = create_packet_from(
|
||||||
|
LiteHeader {
|
||||||
|
destination_port: header.source_port,
|
||||||
|
source_port: param.virtual_port.get_port_number(),
|
||||||
|
stream_types: StreamTypes::new(
|
||||||
|
param.virtual_port.get_stream_type(),
|
||||||
|
header.stream_types.source(),
|
||||||
|
),
|
||||||
|
fragment_id: 0,
|
||||||
|
sequence_id: 0,
|
||||||
|
types_flags: TypesFlags::default().types(CONNECT).flags(ACK),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
&[
|
||||||
|
PacketSpecificData::SupportedFunctions(s & 0xFF),
|
||||||
|
PacketSpecificData::ConnectionSignature([0; 16]),
|
||||||
|
],
|
||||||
|
&data,
|
||||||
|
);
|
||||||
|
websocket.send(Message::Binary(data.into())).await;
|
||||||
|
|
||||||
|
let addr = PRUDPSockAddr::new(
|
||||||
|
addr,
|
||||||
|
VirtualPort::new(header.source_port, header.stream_types.source()),
|
||||||
|
);
|
||||||
|
let Some(backend_conn) = new_backend_connection(¶m, addr, pid).await
|
||||||
|
else {
|
||||||
|
error!("unable to connect to backend");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let mut connection = ConnectionState {
|
||||||
|
active: true,
|
||||||
|
addr,
|
||||||
|
pid,
|
||||||
|
backend_conn,
|
||||||
|
client_reliable_counter: 2,
|
||||||
|
server_reliable_counter: 1,
|
||||||
|
param,
|
||||||
|
incoming_reliable: HashMap::new(),
|
||||||
|
websocket,
|
||||||
|
};
|
||||||
|
|
||||||
|
connection.handle_connection().await;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
v => {
|
||||||
|
error!(
|
||||||
|
"invalid packet type for unconnected client {}, disconnecting",
|
||||||
|
v,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v => {
|
||||||
|
error!("non binary message({:?}) , disconnecting", v);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn start_proxy<C: Crypto>(param: ProxyStartupParam) {
|
||||||
|
let param = Arc::new(param);
|
||||||
|
let crypto = Arc::new(C::new());
|
||||||
|
let listener = TcpListener::bind(param.self_private)
|
||||||
|
.await
|
||||||
|
.expect("unable to bind to port");
|
||||||
|
|
||||||
|
while let Ok((connection, addr)) = listener.accept().await {
|
||||||
|
let param = param.clone();
|
||||||
|
let crypto = crypto.clone();
|
||||||
|
tokio::spawn(websocket_thread_unconnected(
|
||||||
|
param, crypto, connection, addr,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn start_secure(param: ProxyStartupParam) {
|
||||||
|
start_proxy::<Secure>(param).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn start_insecure(param: ProxyStartupParam) {
|
||||||
|
start_proxy::<Insecure>(param).await;
|
||||||
|
}
|
||||||
45
prudplite/src/main.rs
Normal file
45
prudplite/src/main.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
use futures_util::{SinkExt, StreamExt};
|
||||||
|
use rnex_core::prudp::types_flags::{TypesFlags, flags::NEED_ACK, types::SYN};
|
||||||
|
use tokio_tungstenite::tungstenite::{Message, client::IntoClientRequest, http::header};
|
||||||
|
|
||||||
|
use crate::packet::{LiteHeader, LitePacket, PacketSpecificData, StreamTypes, create_packet_from};
|
||||||
|
|
||||||
|
mod packet;
|
||||||
|
|
||||||
|
const KEY: &str = "4eb18d39";
|
||||||
|
|
||||||
|
const URL: &str = "wss://g2DF33D01-lp1.s.n.srv.nintendo.net";
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let login = URL.into_client_request().unwrap();
|
||||||
|
let (mut stream, response) = tokio_tungstenite::connect_async(login).await.unwrap();
|
||||||
|
|
||||||
|
println!("response: {:?}", response);
|
||||||
|
|
||||||
|
let packet = create_packet_from(
|
||||||
|
LiteHeader {
|
||||||
|
stream_types: StreamTypes::new(10, 10),
|
||||||
|
source_port: 1,
|
||||||
|
destination_port: 1,
|
||||||
|
fragment_id: 0,
|
||||||
|
types_flags: TypesFlags::default().types(SYN).flags(NEED_ACK),
|
||||||
|
sequence_id: 0,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
&[PacketSpecificData::SupportedFunctions(0x8)],
|
||||||
|
&[],
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("sending ack");
|
||||||
|
stream.send(Message::Binary(packet.into())).await.unwrap();
|
||||||
|
println!("waiting for response");
|
||||||
|
let packet = stream.next().await.unwrap();
|
||||||
|
let Message::Binary(packet) = packet.unwrap() else {
|
||||||
|
panic!()
|
||||||
|
};
|
||||||
|
let packet = LitePacket::new(packet);
|
||||||
|
|
||||||
|
let header = packet.header().unwrap();
|
||||||
|
|
||||||
|
println!("{:?}", header);
|
||||||
|
}
|
||||||
223
prudplite/src/packet.rs
Normal file
223
prudplite/src/packet.rs
Normal file
|
|
@ -0,0 +1,223 @@
|
||||||
|
use std::{
|
||||||
|
fmt::Debug,
|
||||||
|
io::{self, Cursor, Read, Write},
|
||||||
|
};
|
||||||
|
|
||||||
|
use bytemuck::{Pod, Zeroable, bytes_of};
|
||||||
|
use futures_util::Stream;
|
||||||
|
use rnex_core::prudp::types_flags::TypesFlags;
|
||||||
|
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||||
|
|
||||||
|
#[derive(Pod, Zeroable, Copy, Clone, Default, Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct LiteHeader {
|
||||||
|
pub magic: u8,
|
||||||
|
pub packet_specific_length: u8,
|
||||||
|
pub payload_size: u16,
|
||||||
|
pub stream_types: StreamTypes,
|
||||||
|
pub source_port: u8,
|
||||||
|
pub destination_port: u8,
|
||||||
|
pub fragment_id: u8,
|
||||||
|
pub types_flags: TypesFlags,
|
||||||
|
pub sequence_id: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum PacketSpecificData {
|
||||||
|
SupportedFunctions(u32),
|
||||||
|
ConnectionSignature([u8; 16]),
|
||||||
|
LiteSignature([u8; 16]),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketSpecificData {
|
||||||
|
fn consume(reader: &mut impl Read) -> io::Result<Self> {
|
||||||
|
let mut option_id = 0;
|
||||||
|
reader.read_exact(&mut [option_id])?;
|
||||||
|
let mut size = 0;
|
||||||
|
reader.read_exact(&mut [size])?;
|
||||||
|
|
||||||
|
match option_id {
|
||||||
|
0 => {
|
||||||
|
if size != 4 {
|
||||||
|
Err(io::Error::other(
|
||||||
|
"invalid option size for supported functions",
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(Self::SupportedFunctions(reader.read_le_u32()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
if size != 16 {
|
||||||
|
Err(io::Error::other(
|
||||||
|
"invalid option size for connection signature",
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(Self::ConnectionSignature(
|
||||||
|
reader.read_struct(IS_BIG_ENDIAN)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0x80 => {
|
||||||
|
if size != 16 {
|
||||||
|
Err(io::Error::other("invalid option size for lite signature"))
|
||||||
|
} else {
|
||||||
|
Ok(Self::LiteSignature(reader.read_struct(IS_BIG_ENDIAN)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(io::Error::other("invalid option id")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_size(&self) -> usize {
|
||||||
|
2 + match self {
|
||||||
|
PacketSpecificData::SupportedFunctions(_) => 4,
|
||||||
|
Self::ConnectionSignature(_) => 16,
|
||||||
|
Self::LiteSignature(_) => 16,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_self(&self, writer: &mut impl Write) -> io::Result<()> {
|
||||||
|
match self {
|
||||||
|
PacketSpecificData::SupportedFunctions(v) => {
|
||||||
|
writer.write_all(&[0, 4])?;
|
||||||
|
writer.write_all(&v.to_le_bytes())?;
|
||||||
|
}
|
||||||
|
Self::ConnectionSignature(v) => {
|
||||||
|
writer.write_all(&[1, 16])?;
|
||||||
|
writer.write_all(&v[..])?;
|
||||||
|
}
|
||||||
|
Self::LiteSignature(v) => {
|
||||||
|
writer.write_all(&[0x80, 16])?;
|
||||||
|
writer.write_all(&v[..])?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LitePacket<T: AsRef<[u8]>>(T);
|
||||||
|
|
||||||
|
pub struct PacketSpecificIter<'a>(Cursor<&'a [u8]>);
|
||||||
|
|
||||||
|
impl<'a> Iterator for PacketSpecificIter<'a> {
|
||||||
|
type Item = PacketSpecificData;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
PacketSpecificData::consume(&mut self.0).ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsRef<[u8]>> LitePacket<T> {
|
||||||
|
pub fn new(inner: T) -> Self {
|
||||||
|
Self(inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn header(&self) -> Option<&LiteHeader> {
|
||||||
|
bytemuck::try_from_bytes(self.0.as_ref().get(..size_of::<LiteHeader>())?).ok()
|
||||||
|
}
|
||||||
|
pub fn header_mut(&mut self) -> Option<&mut LiteHeader>
|
||||||
|
where
|
||||||
|
T: AsMut<[u8]>,
|
||||||
|
{
|
||||||
|
bytemuck::try_from_bytes_mut(self.0.as_mut().get_mut(..size_of::<LiteHeader>())?).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn payload(&self) -> Option<&[u8]> {
|
||||||
|
let header = self.header()?;
|
||||||
|
self.0
|
||||||
|
.as_ref()
|
||||||
|
.get(size_of::<LiteHeader>() + header.packet_specific_length as usize..)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn payload_mut(&mut self) -> Option<&mut [u8]>
|
||||||
|
where
|
||||||
|
T: AsMut<[u8]>,
|
||||||
|
{
|
||||||
|
let len = self.header()?.packet_specific_length;
|
||||||
|
self.0
|
||||||
|
.as_mut()
|
||||||
|
.get_mut(size_of::<LiteHeader>() + len as usize..)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn packet_specific_raw(&self) -> Option<&[u8]> {
|
||||||
|
let header = self.header()?;
|
||||||
|
self.0.as_ref().get(
|
||||||
|
size_of::<LiteHeader>()
|
||||||
|
..size_of::<LiteHeader>() + header.packet_specific_length as usize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
pub fn packet_specific_raw_mut(&mut self) -> Option<&mut [u8]>
|
||||||
|
where
|
||||||
|
T: AsMut<[u8]>,
|
||||||
|
{
|
||||||
|
let len = self.header()?.packet_specific_length;
|
||||||
|
self.0
|
||||||
|
.as_mut()
|
||||||
|
.get_mut(size_of::<LiteHeader>()..size_of::<LiteHeader>() + len as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn packet_specific_iter(&self) -> Option<PacketSpecificIter> {
|
||||||
|
self.packet_specific_raw()
|
||||||
|
.map(Cursor::new)
|
||||||
|
.map(PacketSpecificIter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_packet_from(
|
||||||
|
header: LiteHeader,
|
||||||
|
specific_data: &[PacketSpecificData],
|
||||||
|
data: &[u8],
|
||||||
|
) -> Vec<u8> {
|
||||||
|
let specific_size: usize = specific_data.iter().map(|v| v.write_size()).sum();
|
||||||
|
let mut packet = LitePacket::new(vec![
|
||||||
|
0u8;
|
||||||
|
size_of::<LiteHeader>() + specific_size + data.len()
|
||||||
|
]);
|
||||||
|
|
||||||
|
*packet.header_mut().expect("packet malformed in creation") = LiteHeader {
|
||||||
|
magic: 0x80,
|
||||||
|
packet_specific_length: specific_size as u8,
|
||||||
|
payload_size: data.len() as u16,
|
||||||
|
..header
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut cursor = Cursor::new(
|
||||||
|
packet
|
||||||
|
.packet_specific_raw_mut()
|
||||||
|
.expect("packet malformed in creation"),
|
||||||
|
);
|
||||||
|
|
||||||
|
for specific in specific_data {
|
||||||
|
specific.write_self(&mut cursor).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
packet
|
||||||
|
.payload_mut()
|
||||||
|
.expect("packet malformed in creation")
|
||||||
|
.copy_from_slice(data);
|
||||||
|
|
||||||
|
packet.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Pod, Zeroable, Copy, Clone, Default)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct StreamTypes(u8);
|
||||||
|
|
||||||
|
impl Debug for StreamTypes {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "({},{})", self.source(), self.destination())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StreamTypes {
|
||||||
|
pub fn new(source_stream: u8, dest_stream: u8) -> Self {
|
||||||
|
Self((source_stream & 0xF << 4) & dest_stream & 0xF)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source(&self) -> u8 {
|
||||||
|
self.0 >> 4
|
||||||
|
}
|
||||||
|
pub fn destination(&self) -> u8 {
|
||||||
|
self.0 & 0xF
|
||||||
|
}
|
||||||
|
}
|
||||||
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
|
||||||
|
}
|
||||||
6
prudpv0/src/crypto/friends_common.rs
Normal file
6
prudpv0/src/crypto/friends_common.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
use hmac::Hmac;
|
||||||
|
use md5::Md5;
|
||||||
|
use proxy_common::RNEX_ACCESS_KEY;
|
||||||
|
|
||||||
|
pub const ACCESS_KEY: &str = RNEX_ACCESS_KEY;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
406
prudpv0/src/packet.rs
Normal file
406
prudpv0/src/packet.rs
Normal file
|
|
@ -0,0 +1,406 @@
|
||||||
|
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"),
|
||||||
|
);
|
||||||
|
info!("header: {:?}", packet.header());
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
523
prudpv0/src/server.rs
Normal file
523
prudpv0/src/server.rs
Normal file
|
|
@ -0,0 +1,523 @@
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
hash::Hash,
|
||||||
|
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
|
||||||
|
sync::{
|
||||||
|
Arc, LazyLock, Weak,
|
||||||
|
atomic::{AtomicBool, AtomicU32},
|
||||||
|
},
|
||||||
|
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, sleep},
|
||||||
|
};
|
||||||
|
|
||||||
|
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, u8), 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 pieces = data.chunks(700);
|
||||||
|
let max_piece = pieces.len() - 1;
|
||||||
|
let mut frag_num = 1;
|
||||||
|
for (i, piece) in pieces.enumerate() {
|
||||||
|
let seq = inner.server_packet_counter;
|
||||||
|
let packet = new_data_packet(
|
||||||
|
NEED_ACK | RELIABLE,
|
||||||
|
(&self).param.virtual_port,
|
||||||
|
conn.addr.virtual_port,
|
||||||
|
piece,
|
||||||
|
inner.server_packet_counter,
|
||||||
|
conn.session_id,
|
||||||
|
if i == max_piece { 0 } else { frag_num },
|
||||||
|
&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);
|
||||||
|
|
||||||
|
let conn = Arc::downgrade(&conn);
|
||||||
|
let this = Arc::downgrade(&self);
|
||||||
|
|
||||||
|
spawn(async move {
|
||||||
|
sleep(Duration::from_millis(i as u64 * 16)).await;
|
||||||
|
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);
|
||||||
|
|
||||||
|
this.socket
|
||||||
|
.send_to(&data, conn.addr.regular_socket_addr)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
frag_num += 1;
|
||||||
|
}
|
||||||
|
drop(inner);
|
||||||
|
}
|
||||||
|
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: Weak<Connection<C::Instance>>) {
|
||||||
|
loop {
|
||||||
|
let Some(conn) = conn.upgrade() else { break };
|
||||||
|
sleep(Duration::from_secs(3)).await;
|
||||||
|
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, conn.session_id));
|
||||||
|
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();
|
||||||
|
println!("user with pid {} is connecting", pid);
|
||||||
|
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;
|
||||||
|
if conns.contains_key(&(addr, header.session_id)) {
|
||||||
|
error!("client already connected but tried to connect again");
|
||||||
|
}
|
||||||
|
conns.insert((addr, header.session_id), 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 = Arc::downgrade(&conn);
|
||||||
|
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, header.session_id)).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;
|
||||||
|
let packet = conn.packet_queue.remove(&ctr);
|
||||||
|
packet
|
||||||
|
} {
|
||||||
|
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, header.session_id)).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, header.session_id)).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);
|
||||||
|
|
||||||
|
let mut conns = self.connections.write().await;
|
||||||
|
conns.remove(&(addr, header.session_id));
|
||||||
|
drop(conns);
|
||||||
|
|
||||||
|
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, u8),
|
||||||
|
) -> 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(SocketAddr::V4(addr), header.source);
|
||||||
|
|
||||||
|
if let Some(conn) = self.get_connection((addr, header.session_id)).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,50 @@
|
||||||
|
use crate::executables::common::{EDGE_NODE_HOLDER, FORWARD_DESTINATION};
|
||||||
|
use crate::prudp::router::Router;
|
||||||
|
use crate::prudp::unsecure::Unsecure;
|
||||||
|
use log::error;
|
||||||
|
use rnex_core::common::setup;
|
||||||
|
use rnex_core::executables::common::{OWN_IP_PRIVATE, OWN_IP_PUBLIC, SERVER_PORT};
|
||||||
|
use rnex_core::prudp::virtual_port::VirtualPort;
|
||||||
|
use rnex_core::reggie::EdgeNodeHolderConnectOption::Register;
|
||||||
|
use rnex_core::reggie::RemoteEdgeNodeHolder;
|
||||||
|
use rnex_core::reggie::UnitPacketRead;
|
||||||
|
use rnex_core::reggie::UnitPacketWrite;
|
||||||
|
use rnex_core::rmc::protocols::{OnlyRemote, new_rmc_gateway_connection};
|
||||||
|
use rnex_core::rmc::structures::RmcSerialize;
|
||||||
|
use rnex_core::rnex_proxy_common::ConnectionInitData;
|
||||||
|
use rnex_core::util::SplittableBufferConnection;
|
||||||
use std::net::SocketAddrV4;
|
use std::net::SocketAddrV4;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use futures::future::Remote;
|
|
||||||
use log::{error, warn};
|
|
||||||
use macros::rmc_struct;
|
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio::sync::RwLock;
|
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
use tokio_rustls::client::TlsStream;
|
use proxy_common::RNEX_ACCESS_KEY;
|
||||||
use tokio_tungstenite::MaybeTlsStream;
|
|
||||||
use rust_nex::common::setup;
|
|
||||||
use rust_nex::executables::common::{AUTH_SERVER_ACCOUNT, FORWARD_DESTINATION, OWN_IP_PRIVATE, OWN_IP_PUBLIC, SECURE_EDGE_NODE_HOLDER, SECURE_SERVER_ACCOUNT, SERVER_PORT};
|
|
||||||
use rust_nex::prudp::packet::VirtualPort;
|
|
||||||
use rust_nex::prudp::router::Router;
|
|
||||||
use rust_nex::prudp::secure::Secure;
|
|
||||||
use rust_nex::prudp::unsecure::Unsecure;
|
|
||||||
use rust_nex::reggie::EdgeNodeHolderConnectOption::{DontRegister, Register};
|
|
||||||
use rust_nex::rmc::response::ErrorCode;
|
|
||||||
use rust_nex::rnex_proxy_common::ConnectionInitData;
|
|
||||||
use rust_nex::reggie::{RemoteEdgeNodeHolder, UnitPacketWrite};
|
|
||||||
use rust_nex::rmc::structures::RmcSerialize;
|
|
||||||
use rust_nex::reggie::UnitPacketRead;
|
|
||||||
use rust_nex::rmc::protocols::{new_rmc_gateway_connection, OnlyRemote, RemoteInstantiatable};
|
|
||||||
use rust_nex::util::SplittableBufferConnection;
|
|
||||||
|
|
||||||
#[tokio::main]
|
pub async fn start() {
|
||||||
async fn main() {
|
/*let conn = tokio::net::TcpStream::connect(&*EDGE_NODE_HOLDER)
|
||||||
setup();
|
.await
|
||||||
|
.unwrap();
|
||||||
let conn = tokio::net::TcpStream::connect(&*SECURE_EDGE_NODE_HOLDER).await.unwrap();
|
|
||||||
|
|
||||||
let conn: SplittableBufferConnection = conn.into();
|
let conn: SplittableBufferConnection = conn.into();
|
||||||
|
|
||||||
conn.send(Register(SocketAddrV4::new(*OWN_IP_PUBLIC, *SERVER_PORT)).to_data()).await;
|
conn.send(
|
||||||
|
Register(SocketAddrV4::new(*OWN_IP_PUBLIC, *SERVER_PORT))
|
||||||
let conn = new_rmc_gateway_connection(conn, |r| Arc::new(OnlyRemote::<RemoteEdgeNodeHolder>::new(r)));
|
.to_data()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let conn = new_rmc_gateway_connection(conn, |r| {
|
||||||
|
Arc::new(OnlyRemote::<RemoteEdgeNodeHolder>::new(r))
|
||||||
|
});*/
|
||||||
|
|
||||||
let (router_secure, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *SERVER_PORT))
|
let (router_secure, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *SERVER_PORT))
|
||||||
.await
|
.await
|
||||||
.expect("unable to start router");
|
.expect("unable to start router");
|
||||||
|
|
||||||
let mut socket_secure = router_secure
|
let mut socket_secure = router_secure
|
||||||
.add_socket(VirtualPort::new(1, 10), Secure(
|
.add_socket(VirtualPort::new(1, 10), Unsecure(RNEX_ACCESS_KEY))
|
||||||
"6f599f81",
|
|
||||||
SECURE_SERVER_ACCOUNT.clone()
|
|
||||||
))
|
|
||||||
.await
|
.await
|
||||||
.expect("unable to add socket");
|
.expect("unable to add socket");
|
||||||
|
|
||||||
|
|
@ -60,8 +57,7 @@ async fn main() {
|
||||||
};
|
};
|
||||||
|
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
let mut stream
|
let mut stream = match TcpStream::connect(*FORWARD_DESTINATION).await {
|
||||||
= match TcpStream::connect(*FORWARD_DESTINATION).await {
|
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("unable to connect: {}", e);
|
error!("unable to connect: {}", e);
|
||||||
|
|
@ -69,26 +65,31 @@ async fn main() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = stream.send_buffer(&ConnectionInitData{
|
if let Err(e) = stream
|
||||||
prudpsock_addr: conn.socket_addr,
|
.send_buffer(
|
||||||
pid: conn.user_id
|
&ConnectionInitData {
|
||||||
}.to_data()).await{
|
prudpsock_addr: conn.socket_addr,
|
||||||
|
pid: conn.user_id,
|
||||||
|
}
|
||||||
|
.to_data()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
error!("error connecting to backend: {}", e);
|
error!("error connecting to backend: {}", e);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
data = conn.recv() => {
|
data = conn.recv() => {
|
||||||
let Some(data) = data else {
|
let Some(data) = data else {
|
||||||
break;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = stream.send_buffer(&data[..]).await{
|
if let Err(e) = stream.send_buffer(&data[..]).await{
|
||||||
error!("error sending data to backend: {}", e);
|
error!("error sending data to backend: {}", e);
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data = stream.read_buffer() => {
|
data = stream.read_buffer() => {
|
||||||
|
|
@ -96,10 +97,10 @@ async fn main() {
|
||||||
Ok(d) => d,
|
Ok(d) => d,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("error reveiving data from backend: {}", e);
|
error!("error reveiving data from backend: {}", e);
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if conn.send(data).await == None{
|
if conn.send(data).await == None{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -111,4 +112,4 @@ async fn main() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
103
prudpv1/src/executables/proxy_secure.rs
Normal file
103
prudpv1/src/executables/proxy_secure.rs
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
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;
|
||||||
|
use proxy_common::RNEX_ACCESS_KEY;
|
||||||
|
|
||||||
|
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(RNEX_ACCESS_KEY, 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;
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
pub trait AuthModule{
|
pub trait AuthModule{
|
||||||
fn get_auth_key(addr: Ipv4Addr) -> [u8; 32];
|
fn get_auth_key(addr: Ipv4Addr) -> [u8; 32];
|
||||||
}
|
}*/
|
||||||
/*
|
/*
|
||||||
struct AuthServerAuthModule;
|
struct AuthServerAuthModule;
|
||||||
|
|
||||||
|
|
@ -2,7 +2,5 @@ pub mod packet;
|
||||||
pub mod router;
|
pub mod router;
|
||||||
pub mod socket;
|
pub mod socket;
|
||||||
mod auth_module;
|
mod auth_module;
|
||||||
pub mod sockaddr;
|
|
||||||
pub mod station_url;
|
|
||||||
pub mod secure;
|
pub mod secure;
|
||||||
pub mod unsecure;
|
pub mod unsecure;
|
||||||
|
|
@ -3,20 +3,25 @@
|
||||||
// force the compiler to shut up here
|
// force the compiler to shut up here
|
||||||
#![allow(unused_parens)]
|
#![allow(unused_parens)]
|
||||||
|
|
||||||
use std::fmt::{Debug, Formatter};
|
use crate::prudp::packet::PacketOption::{
|
||||||
use std::io;
|
ConnectionSignature, FragmentId, InitialSequenceId, MaximumSubstreamId, SupportedFunctions,
|
||||||
use std::io::{Cursor, Read, Seek, Write};
|
};
|
||||||
use std::net::SocketAddrV4;
|
|
||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
use hmac::{Hmac, Mac};
|
use hmac::{Hmac, Mac};
|
||||||
use log::{error, warn};
|
use log::{error, warn};
|
||||||
use md5::{Md5, Digest};
|
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::SocketAddr;
|
||||||
|
use std::net::SocketAddrV4;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use v_byte_macros::{SwapEndian};
|
use v_byte_helpers::SwapEndian;
|
||||||
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
|
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||||
use crate::prudp::packet::flags::ACK;
|
|
||||||
use crate::prudp::packet::PacketOption::{ConnectionSignature, FragmentId, InitialSequenceId, MaximumSubstreamId, SupportedFunctions};
|
|
||||||
use crate::prudp::sockaddr::PRUDPSockAddr;
|
|
||||||
|
|
||||||
type Md5Hmac = Hmac<Md5>;
|
type Md5Hmac = Hmac<Md5>;
|
||||||
|
|
||||||
|
|
@ -31,117 +36,11 @@ pub enum Error {
|
||||||
#[error("invalid option id {0}")]
|
#[error("invalid option id {0}")]
|
||||||
InvalidOptionId(u8),
|
InvalidOptionId(u8),
|
||||||
#[error("option size {size} doesnt match expected option for given option id {id}")]
|
#[error("option size {size} doesnt match expected option for given option id {id}")]
|
||||||
InvalidOptionSize {
|
InvalidOptionSize { id: u8, size: u8 },
|
||||||
id: u8,
|
|
||||||
size: u8,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[derive(Copy, Clone, Pod, Zeroable, SwapEndian, Default, Eq, PartialEq)]
|
|
||||||
pub struct TypesFlags(u16);
|
|
||||||
|
|
||||||
impl TypesFlags {
|
|
||||||
#[inline]
|
|
||||||
pub const fn get_types(self) -> u8 {
|
|
||||||
(self.0 & 0x000F) as u8
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub const fn get_flags(self) -> u16 {
|
|
||||||
(self.0 & 0xFFF0) >> 4
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub const fn types(self, val: u8) -> Self {
|
|
||||||
Self((self.0 & 0xFFF0) | (val as u16 & 0x000F))
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub const fn flags(self, val: u16) -> Self {
|
|
||||||
Self((self.0 & 0x000F) | ((val << 4) & 0xFFF0))
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub const fn set_flag(&mut self, val: u16){
|
|
||||||
self.0 |= (val & 0xFFF) << 4;
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub const fn set_types(&mut self, val: u8){
|
|
||||||
self.0 |= val as u16 & 0x0F;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod flags {
|
|
||||||
pub const ACK: u16 = 0x001;
|
|
||||||
pub const RELIABLE: u16 = 0x002;
|
|
||||||
pub const NEED_ACK: u16 = 0x004;
|
|
||||||
pub const HAS_SIZE: u16 = 0x008;
|
|
||||||
pub const MULTI_ACK: u16 = 0x200;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod types {
|
|
||||||
pub const SYN: u8 = 0x0;
|
|
||||||
pub const CONNECT: u8 = 0x1;
|
|
||||||
pub const DATA: u8 = 0x2;
|
|
||||||
pub const DISCONNECT: u8 = 0x3;
|
|
||||||
pub const PING: u8 = 0x4;
|
|
||||||
/// no idea what user is supposed to mean
|
|
||||||
pub const USER: u8 = 0x5;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for TypesFlags {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let stream_type = self.get_types();
|
|
||||||
let port_number = self.get_flags();
|
|
||||||
write!(f, "TypesFlags{{ types: {}, flags: {} }}", stream_type, port_number)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[derive(PartialEq, Eq, Ord, PartialOrd, Copy, Clone, Pod, Zeroable, SwapEndian, Hash)]
|
|
||||||
pub struct VirtualPort(pub(crate) u8);
|
|
||||||
|
|
||||||
impl VirtualPort {
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub const fn get_stream_type(self) -> u8 {
|
|
||||||
(self.0 & 0xF0) >> 4
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub const fn get_port_number(self) -> u8 {
|
|
||||||
self.0 & 0x0F
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn stream_type(self, val: u8) -> Self {
|
|
||||||
let masked_val = val & 0x0F;
|
|
||||||
assert_eq!(masked_val, val);
|
|
||||||
|
|
||||||
Self((self.0 & 0x0F) | (masked_val << 4))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn port_number(self, val: u8) -> Self {
|
|
||||||
let masked_val = val & 0x0F;
|
|
||||||
assert_eq!(masked_val, val);
|
|
||||||
|
|
||||||
Self((self.0 & 0xF0) | masked_val)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn new(port: u8, stream_type: u8) -> Self {
|
|
||||||
Self(0).stream_type(stream_type).port_number(port)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for VirtualPort {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let stream_type = self.get_stream_type();
|
|
||||||
let port_number = self.get_port_number();
|
|
||||||
write!(f, "VirtualPort{{ stream_type: {}, port_number: {} }}", stream_type, port_number)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone, Pod, Zeroable, SwapEndian, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Pod, Zeroable, SwapEndian, Eq, PartialEq)]
|
||||||
pub struct PRUDPV1Header {
|
pub struct PRUDPV1Header {
|
||||||
|
|
@ -159,7 +58,7 @@ pub struct PRUDPV1Header {
|
||||||
|
|
||||||
impl Default for PRUDPV1Header {
|
impl Default for PRUDPV1Header {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self{
|
Self {
|
||||||
magic: [0xEA, 0xD0],
|
magic: [0xEA, 0xD0],
|
||||||
version: 1,
|
version: 1,
|
||||||
session_id: 0,
|
session_id: 0,
|
||||||
|
|
@ -169,32 +68,30 @@ impl Default for PRUDPV1Header {
|
||||||
destination_port: VirtualPort(0),
|
destination_port: VirtualPort(0),
|
||||||
types_and_flags: TypesFlags(0),
|
types_and_flags: TypesFlags(0),
|
||||||
packet_specific_size: 0,
|
packet_specific_size: 0,
|
||||||
substream_id: 0
|
substream_id: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub enum PacketOption{
|
pub enum PacketOption {
|
||||||
SupportedFunctions(u32),
|
SupportedFunctions(u32),
|
||||||
ConnectionSignature([u8; 16]),
|
ConnectionSignature([u8; 16]),
|
||||||
FragmentId(u8),
|
FragmentId(u8),
|
||||||
InitialSequenceId(u16),
|
InitialSequenceId(u16),
|
||||||
MaximumSubstreamId(u8)
|
MaximumSubstreamId(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PacketOption{
|
impl PacketOption {
|
||||||
fn from(option_id: OptionId, option_data: &[u8]) -> io::Result<Self>{
|
fn from(option_id: OptionId, option_data: &[u8]) -> io::Result<Self> {
|
||||||
|
|
||||||
let mut data_cursor = Cursor::new(option_data);
|
let mut data_cursor = Cursor::new(option_data);
|
||||||
let val = match option_id.into(){
|
let val = match option_id.into() {
|
||||||
0 => SupportedFunctions(data_cursor.read_struct(IS_BIG_ENDIAN)?),
|
0 => SupportedFunctions(data_cursor.read_struct(IS_BIG_ENDIAN)?),
|
||||||
1 => ConnectionSignature(data_cursor.read_struct(IS_BIG_ENDIAN)?),
|
1 => ConnectionSignature(data_cursor.read_struct(IS_BIG_ENDIAN)?),
|
||||||
2 => FragmentId(data_cursor.read_struct(IS_BIG_ENDIAN)?),
|
2 => FragmentId(data_cursor.read_struct(IS_BIG_ENDIAN)?),
|
||||||
3 => InitialSequenceId(data_cursor.read_struct(IS_BIG_ENDIAN)?),
|
3 => InitialSequenceId(data_cursor.read_struct(IS_BIG_ENDIAN)?),
|
||||||
4 => MaximumSubstreamId(data_cursor.read_struct(IS_BIG_ENDIAN)?),
|
4 => MaximumSubstreamId(data_cursor.read_struct(IS_BIG_ENDIAN)?),
|
||||||
_ => unreachable!()
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(val)
|
Ok(val)
|
||||||
|
|
@ -255,7 +152,7 @@ impl OptionId {
|
||||||
// Invariant is upheld because we only create the object if it doesn't violate the invariant
|
// Invariant is upheld because we only create the object if it doesn't violate the invariant
|
||||||
match val {
|
match val {
|
||||||
0 | 1 | 2 | 3 | 4 => Ok(Self(val)),
|
0 | 1 | 2 | 3 | 4 => Ok(Self(val)),
|
||||||
_ => Err(Error::InvalidOptionId(val))
|
_ => Err(Error::InvalidOptionId(val)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -266,7 +163,7 @@ impl OptionId {
|
||||||
2 => 1,
|
2 => 1,
|
||||||
3 => 2,
|
3 => 2,
|
||||||
4 => 1,
|
4 => 1,
|
||||||
_ => unreachable!()
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -281,8 +178,7 @@ impl PRUDPV1Packet {
|
||||||
pub fn new(reader: &mut (impl Read + Seek)) -> Result<Self> {
|
pub fn new(reader: &mut (impl Read + Seek)) -> Result<Self> {
|
||||||
let header: PRUDPV1Header = reader.read_struct(IS_BIG_ENDIAN)?;
|
let header: PRUDPV1Header = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||||
|
|
||||||
if header.magic[0] != 0xEA ||
|
if header.magic[0] != 0xEA || header.magic[1] != 0xD0 {
|
||||||
header.magic[1] != 0xD0 {
|
|
||||||
return Err(Error::InvalidMagic(u16::from_be_bytes(header.magic)));
|
return Err(Error::InvalidMagic(u16::from_be_bytes(header.magic)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,31 +186,31 @@ impl PRUDPV1Packet {
|
||||||
return Err(Error::InvalidVersion(header.version));
|
return Err(Error::InvalidVersion(header.version));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let packet_signature: [u8; 16] = reader.read_struct(IS_BIG_ENDIAN)?;
|
let packet_signature: [u8; 16] = reader.read_struct(IS_BIG_ENDIAN)?;
|
||||||
//let packet_signature: [u8; 16] = [0; 16];
|
//let packet_signature: [u8; 16] = [0; 16];
|
||||||
|
|
||||||
assert_eq!(reader.stream_position().ok(), Some(14 + 16));
|
assert_eq!(reader.stream_position().ok(), Some(14 + 16));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let mut packet_specific_buffer = vec![0u8; header.packet_specific_size as usize];
|
let mut packet_specific_buffer = vec![0u8; header.packet_specific_size as usize];
|
||||||
|
|
||||||
reader.read_exact(&mut packet_specific_buffer)?;
|
reader.read_exact(&mut packet_specific_buffer)?;
|
||||||
|
|
||||||
|
|
||||||
//no clue whats up with options but they are broken
|
//no clue whats up with options but they are broken
|
||||||
let mut packet_specific_data_cursor = Cursor::new(&packet_specific_buffer);
|
let mut packet_specific_data_cursor = Cursor::new(&packet_specific_buffer);
|
||||||
|
|
||||||
let mut options = Vec::new();
|
let mut options = Vec::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let Ok(option_id): io::Result<u8> = packet_specific_data_cursor.read_struct(IS_BIG_ENDIAN) else {
|
let Ok(option_id): io::Result<u8> =
|
||||||
break
|
packet_specific_data_cursor.read_struct(IS_BIG_ENDIAN)
|
||||||
|
else {
|
||||||
|
break;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Ok(value_size): io::Result<u8> = packet_specific_data_cursor.read_struct(IS_BIG_ENDIAN) else {
|
let Ok(value_size): io::Result<u8> =
|
||||||
break
|
packet_specific_data_cursor.read_struct(IS_BIG_ENDIAN)
|
||||||
|
else {
|
||||||
|
break;
|
||||||
};
|
};
|
||||||
|
|
||||||
if value_size == 0 {
|
if value_size == 0 {
|
||||||
|
|
@ -334,20 +230,21 @@ impl PRUDPV1Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut option_data = vec![0u8; value_size as usize];
|
let mut option_data = vec![0u8; value_size as usize];
|
||||||
if packet_specific_data_cursor.read_exact(&mut option_data[..]).is_err() {
|
if packet_specific_data_cursor
|
||||||
|
.read_exact(&mut option_data[..])
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
error!("unable to read options");
|
error!("unable to read options");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
options.push(PacketOption::from(option_id, &option_data)?);
|
options.push(PacketOption::from(option_id, &option_data)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut payload = vec![0u8; header.payload_size as usize];
|
let mut payload = vec![0u8; header.payload_size as usize];
|
||||||
|
|
||||||
reader.read_exact(&mut payload)?;
|
reader.read_exact(&mut payload)?;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
header,
|
header,
|
||||||
packet_signature,
|
packet_signature,
|
||||||
|
|
@ -356,22 +253,21 @@ impl PRUDPV1Packet {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn base_acknowledgement_packet(&self) -> Self{
|
pub fn base_acknowledgement_packet(&self) -> Self {
|
||||||
let base = self.base_response_packet();
|
let base = self.base_response_packet();
|
||||||
|
|
||||||
let mut flags = self.header.types_and_flags.flags(0);
|
let mut flags = self.header.types_and_flags.flags(0);
|
||||||
|
|
||||||
flags.set_flag(ACK);
|
flags.set_flag(ACK);
|
||||||
|
|
||||||
let options = self.options
|
let options = self
|
||||||
|
.options
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|o| matches!(o, FragmentId(_)))
|
.filter(|o| matches!(o, FragmentId(_)))
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
|
||||||
Self{
|
|
||||||
header: PRUDPV1Header {
|
header: PRUDPV1Header {
|
||||||
types_and_flags: flags,
|
types_and_flags: flags,
|
||||||
sequence_id: self.header.sequence_id,
|
sequence_id: self.header.sequence_id,
|
||||||
|
|
@ -386,22 +282,29 @@ impl PRUDPV1Packet {
|
||||||
|
|
||||||
pub fn source_sockaddr(&self, socket_addr_v4: SocketAddrV4) -> PRUDPSockAddr {
|
pub fn source_sockaddr(&self, socket_addr_v4: SocketAddrV4) -> PRUDPSockAddr {
|
||||||
PRUDPSockAddr {
|
PRUDPSockAddr {
|
||||||
regular_socket_addr: socket_addr_v4,
|
regular_socket_addr: SocketAddr::V4(socket_addr_v4),
|
||||||
virtual_port: self.header.source_port,
|
virtual_port: self.header.source_port,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_options_bytes(&self) -> Vec<u8>{
|
fn generate_options_bytes(&self) -> Vec<u8> {
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
|
|
||||||
for option in &self.options{
|
for option in &self.options {
|
||||||
option.write_to_stream(&mut vec).expect("vec should always automatically be able to extend");
|
option
|
||||||
|
.write_to_stream(&mut vec)
|
||||||
|
.expect("vec should always automatically be able to extend");
|
||||||
}
|
}
|
||||||
|
|
||||||
vec
|
vec
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_signature_value(&self, access_key: &str, session_key: Option<[u8; 32]>, connection_signature: Option<[u8; 16]>) -> [u8; 16]{
|
pub fn calculate_signature_value(
|
||||||
|
&self,
|
||||||
|
access_key: &str,
|
||||||
|
session_key: Option<[u8; 32]>,
|
||||||
|
connection_signature: Option<[u8; 16]>,
|
||||||
|
) -> [u8; 16] {
|
||||||
let access_key_bytes = access_key.as_bytes();
|
let access_key_bytes = access_key.as_bytes();
|
||||||
let access_key_sum: u32 = access_key_bytes.iter().map(|v| *v as u32).sum();
|
let access_key_sum: u32 = access_key_bytes.iter().map(|v| *v as u32).sum();
|
||||||
let access_key_sum_bytes: [u8; 4] = access_key_sum.to_le_bytes();
|
let access_key_sum_bytes: [u8; 4] = access_key_sum.to_le_bytes();
|
||||||
|
|
@ -417,32 +320,46 @@ impl PRUDPV1Packet {
|
||||||
|
|
||||||
let mut hmac = Md5Hmac::new_from_slice(&key).expect("fuck");
|
let mut hmac = Md5Hmac::new_from_slice(&key).expect("fuck");
|
||||||
|
|
||||||
hmac.write(&header_data).expect("error during hmac calculation");
|
hmac.write(&header_data)
|
||||||
|
.expect("error during hmac calculation");
|
||||||
if let Some(session_key) = session_key {
|
if let Some(session_key) = session_key {
|
||||||
hmac.write(&session_key).expect("error during hmac calculation");
|
hmac.write(&session_key)
|
||||||
|
.expect("error during hmac calculation");
|
||||||
}
|
}
|
||||||
hmac.write(&access_key_sum_bytes).expect("error during hmac calculation");
|
hmac.write(&access_key_sum_bytes)
|
||||||
|
.expect("error during hmac calculation");
|
||||||
if let Some(connection_signature) = connection_signature {
|
if let Some(connection_signature) = connection_signature {
|
||||||
hmac.write(&connection_signature).expect("error during hmac calculation");
|
hmac.write(&connection_signature)
|
||||||
|
.expect("error during hmac calculation");
|
||||||
}
|
}
|
||||||
|
|
||||||
hmac.write(&option_bytes).expect("error during hmac calculation");
|
hmac.write(&option_bytes)
|
||||||
|
.expect("error during hmac calculation");
|
||||||
|
|
||||||
hmac.write_all(&self.payload).expect("error during hmac calculation");
|
hmac.write_all(&self.payload)
|
||||||
|
.expect("error during hmac calculation");
|
||||||
|
|
||||||
hmac.finalize().into_bytes()[0..16].try_into().expect("invalid hmac size")
|
hmac.finalize().into_bytes()[0..16]
|
||||||
|
.try_into()
|
||||||
|
.expect("invalid hmac size")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_and_assign_signature(&mut self, access_key: &str, session_key: Option<[u8; 32]>, connection_signature: Option<[u8; 16]>){
|
pub fn calculate_and_assign_signature(
|
||||||
self.packet_signature = self.calculate_signature_value(access_key, session_key, connection_signature);
|
&mut self,
|
||||||
|
access_key: &str,
|
||||||
|
session_key: Option<[u8; 32]>,
|
||||||
|
connection_signature: Option<[u8; 16]>,
|
||||||
|
) {
|
||||||
|
self.packet_signature =
|
||||||
|
self.calculate_signature_value(access_key, session_key, connection_signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_sizes(&mut self){
|
pub fn set_sizes(&mut self) {
|
||||||
self.header.packet_specific_size = self.options.iter().map(|o| o.write_size()).sum();
|
self.header.packet_specific_size = self.options.iter().map(|o| o.write_size()).sum();
|
||||||
self.header.payload_size = self.payload.len() as u16;
|
self.header.payload_size = self.payload.len() as u16;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn base_response_packet(&self) -> Self {
|
pub fn base_response_packet(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
header: PRUDPV1Header {
|
header: PRUDPV1Header {
|
||||||
magic: [0xEA, 0xD0],
|
magic: [0xEA, 0xD0],
|
||||||
|
|
@ -455,19 +372,18 @@ impl PRUDPV1Packet {
|
||||||
sequence_id: 0,
|
sequence_id: 0,
|
||||||
session_id: 0,
|
session_id: 0,
|
||||||
substream_id: 0,
|
substream_id: 0,
|
||||||
|
|
||||||
},
|
},
|
||||||
packet_signature: [0; 16],
|
packet_signature: [0; 16],
|
||||||
payload: Default::default(),
|
payload: Default::default(),
|
||||||
options: Default::default()
|
options: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_to(&self, writer: &mut impl Write) -> io::Result<()>{
|
pub fn write_to(&self, writer: &mut impl Write) -> io::Result<()> {
|
||||||
writer.write_all(bytemuck::bytes_of(&self.header))?;
|
writer.write_all(bytemuck::bytes_of(&self.header))?;
|
||||||
writer.write_all(&self.packet_signature)?;
|
writer.write_all(&self.packet_signature)?;
|
||||||
|
|
||||||
for option in &self.options{
|
for option in &self.options {
|
||||||
option.write_to_stream(writer)?;
|
option.write_to_stream(writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -479,19 +395,24 @@ impl PRUDPV1Packet {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::prudp::packet::flags::{NEED_ACK, RELIABLE};
|
use super::{OptionId, PRUDPV1Header, PacketOption, TypesFlags};
|
||||||
use crate::prudp::packet::types::DATA;
|
use rnex_core::prudp::{
|
||||||
use super::{OptionId, PacketOption, PRUDPV1Header, TypesFlags, VirtualPort};
|
types_flags::{
|
||||||
|
flags::{NEED_ACK, RELIABLE},
|
||||||
|
types::DATA,
|
||||||
|
},
|
||||||
|
virtual_port::VirtualPort,
|
||||||
|
};
|
||||||
#[test]
|
#[test]
|
||||||
fn size_test() {
|
fn size_test() {
|
||||||
assert_eq!(size_of::<PRUDPV1Header>(), 14);
|
assert_eq!(size_of::<PRUDPV1Header>(), 14);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_options(){
|
fn test_options() {
|
||||||
let packet_types = [0,1,2,3,4];
|
let packet_types = [0, 1, 2, 3, 4];
|
||||||
|
|
||||||
for p_type in packet_types{
|
for p_type in packet_types {
|
||||||
let option_id = OptionId::new(p_type).unwrap();
|
let option_id = OptionId::new(p_type).unwrap();
|
||||||
|
|
||||||
let buf = vec![0; option_id.option_type_size() as usize];
|
let buf = vec![0; option_id.option_type_size() as usize];
|
||||||
|
|
@ -505,12 +426,10 @@ mod test {
|
||||||
assert_eq!(write_buf.len() as u8, opt.write_size())
|
assert_eq!(write_buf.len() as u8, opt.write_size())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn header_read(){
|
fn header_read() {
|
||||||
let header = PRUDPV1Header {
|
let header = PRUDPV1Header {
|
||||||
version: 0,
|
version: 0,
|
||||||
destination_port: VirtualPort(0),
|
destination_port: VirtualPort(0),
|
||||||
|
|
@ -520,8 +439,8 @@ mod test {
|
||||||
packet_specific_size: 0,
|
packet_specific_size: 0,
|
||||||
payload_size: 0,
|
payload_size: 0,
|
||||||
sequence_id: 0,
|
sequence_id: 0,
|
||||||
magic: [0xEA,0xD0],
|
magic: [0xEA, 0xD0],
|
||||||
source_port: VirtualPort(0)
|
source_port: VirtualPort(0),
|
||||||
};
|
};
|
||||||
|
|
||||||
let bytes = bytemuck::bytes_of(&header);
|
let bytes = bytemuck::bytes_of(&header);
|
||||||
|
|
@ -532,11 +451,11 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_types_flags(){
|
fn test_types_flags() {
|
||||||
let types = TypesFlags::default().types(DATA).flags(NEED_ACK | RELIABLE);
|
let types = TypesFlags::default().types(DATA).flags(NEED_ACK | RELIABLE);
|
||||||
|
|
||||||
assert_ne!((types.0 >> 4) & NEED_ACK, 0);
|
assert_ne!((types.0 >> 4) & NEED_ACK, 0);
|
||||||
assert_ne!((types.0 >> 4) & RELIABLE, 0);
|
assert_ne!((types.0 >> 4) & RELIABLE, 0);
|
||||||
assert_ne!((types.0 & 0xFF) as u8 & DATA, 0);
|
assert_ne!((types.0 & 0xFF) as u8 & DATA, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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::io::Cursor;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use tokio::net::UdpSocket;
|
use tokio::net::UdpSocket;
|
||||||
use std::net::{SocketAddr, SocketAddrV4};
|
use std::net::{SocketAddr, SocketAddrV4};
|
||||||
use std::net::SocketAddr::V4;
|
use std::net::SocketAddr::V4;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use std::sync::atomic::{AtomicBool};
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::select;
|
use tokio::select;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
use crate::prudp::socket::{new_socket_pair, AnyInternalSocket, CryptoHandler, ExternalSocket};
|
use crate::prudp::socket::{new_socket_pair, AnyInternalSocket, CryptoHandler, ExternalSocket};
|
||||||
use crate::prudp::packet::{PRUDPV1Packet, VirtualPort};
|
use crate::prudp::packet::{PRUDPV1Packet};
|
||||||
|
use rnex_core::prudp::virtual_port::VirtualPort;
|
||||||
use crate::prudp::router::Error::VirtualPortTaken;
|
use crate::prudp::router::Error::VirtualPortTaken;
|
||||||
|
|
||||||
static SERVER_DATAGRAMS: Lazy<u8> = Lazy::new(||{
|
|
||||||
env::var("SERVER_DATAGRAM_COUNT").ok()
|
|
||||||
.and_then(|s| s.parse().ok())
|
|
||||||
.unwrap_or(1)
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
pub struct Router {
|
pub struct Router {
|
||||||
endpoints: RwLock<[Option<Arc<dyn AnyInternalSocket>>; 16]>,
|
endpoints: RwLock<[Option<Arc<dyn AnyInternalSocket>>; 16]>,
|
||||||
running: AtomicBool,
|
//running: AtomicBool,
|
||||||
socket: Arc<UdpSocket>,
|
socket: Arc<UdpSocket>,
|
||||||
_no_outside_construction: PhantomData<()>
|
_no_outside_construction: PhantomData<()>
|
||||||
}
|
}
|
||||||
|
|
@ -113,7 +107,7 @@ impl Router {
|
||||||
|
|
||||||
let own_impl = Router {
|
let own_impl = Router {
|
||||||
endpoints: Default::default(),
|
endpoints: Default::default(),
|
||||||
running: AtomicBool::new(true),
|
// running: AtomicBool::new(true),
|
||||||
socket: socket.clone(),
|
socket: socket.clone(),
|
||||||
_no_outside_construction: Default::default()
|
_no_outside_construction: Default::default()
|
||||||
};
|
};
|
||||||
|
|
@ -1,110 +1,52 @@
|
||||||
use std::io::Cursor;
|
use crate::prudp::packet::PRUDPV1Packet;
|
||||||
|
use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance};
|
||||||
use hmac::digest::consts::U32;
|
use hmac::digest::consts::U32;
|
||||||
use log::error;
|
use log::error;
|
||||||
use rc4::cipher::StreamCipherCoreWrapper;
|
use rc4::cipher::StreamCipherCoreWrapper;
|
||||||
use rc4::{KeyInit, Rc4, Rc4Core, StreamCipher};
|
|
||||||
use rc4::consts::U16;
|
use rc4::consts::U16;
|
||||||
|
use rc4::{KeyInit, Rc4, Rc4Core, StreamCipher};
|
||||||
|
use rnex_core::kerberos::{TicketInternalData, derive_key};
|
||||||
|
use rnex_core::nex::account::Account;
|
||||||
|
use rnex_core::prudp::encryption::EncryptionPair;
|
||||||
|
use rnex_core::prudp::ticket::read_secure_connection_data;
|
||||||
|
use rnex_core::rmc::structures::RmcSerialize;
|
||||||
|
use std::io::Cursor;
|
||||||
use typenum::U5;
|
use typenum::U5;
|
||||||
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
|
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||||
use crate::kerberos::{derive_key, TicketInternalData};
|
|
||||||
use crate::nex::account::Account;
|
|
||||||
use crate::prudp::packet::PRUDPV1Packet;
|
|
||||||
use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance, EncryptionPair};
|
|
||||||
use crate::rmc::structures::RmcSerialize;
|
|
||||||
|
|
||||||
pub fn read_secure_connection_data(data: &[u8], act: &Account) -> Option<([u8; 32], u32, u32)>{
|
|
||||||
let mut cursor = Cursor::new(data);
|
|
||||||
|
|
||||||
let mut ticket_data: Vec<u8> = Vec::deserialize(&mut cursor).ok()?;
|
|
||||||
let mut request_data: Vec<u8> = Vec::deserialize(&mut cursor).ok()?;
|
|
||||||
|
|
||||||
let ticket_data_size = ticket_data.len();
|
|
||||||
|
|
||||||
let ticket_data = &mut ticket_data[0..ticket_data_size-0x10];
|
|
||||||
|
|
||||||
let server_key = derive_key(act.pid, act.kerbros_password);
|
|
||||||
|
|
||||||
let mut rc4: StreamCipherCoreWrapper<Rc4Core<U16>> =
|
|
||||||
Rc4::new_from_slice(&server_key).expect("unable to init rc4 keystream");
|
|
||||||
|
|
||||||
rc4.apply_keystream(ticket_data);
|
|
||||||
|
|
||||||
let ticket_data: &TicketInternalData = match bytemuck::try_from_bytes(ticket_data){
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(e) => {
|
|
||||||
error!("unable to read internal ticket data: {}", e);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// todo: add ticket expiration
|
|
||||||
|
|
||||||
let TicketInternalData{
|
|
||||||
session_key,
|
|
||||||
pid: ticket_source_pid,
|
|
||||||
issued_time
|
|
||||||
} = *ticket_data;
|
|
||||||
|
|
||||||
// todo: add checking if tickets are signed with a valid md5-hmac
|
|
||||||
let request_data_length = request_data.len();
|
|
||||||
let request_data = &mut request_data[0.. request_data_length - 0x10];
|
|
||||||
|
|
||||||
let mut rc4: StreamCipherCoreWrapper<Rc4Core<U32>> =
|
|
||||||
Rc4::new_from_slice(&session_key).expect("unable to init rc4 keystream");
|
|
||||||
|
|
||||||
rc4.apply_keystream(request_data);
|
|
||||||
|
|
||||||
let mut reqest_data_cursor = Cursor::new(request_data);
|
|
||||||
|
|
||||||
let pid: u32 = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?;
|
|
||||||
|
|
||||||
if pid != ticket_source_pid{
|
|
||||||
let ticket_created_on = issued_time.to_regular_time();
|
|
||||||
|
|
||||||
error!("someone tried to spoof their pid, ticket was created on: {}", ticket_created_on.to_rfc2822());
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let _cid: u32 = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?;
|
|
||||||
let response_check: u32 = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Some((session_key, pid, response_check))
|
|
||||||
}
|
|
||||||
|
|
||||||
type Rc4U32 = StreamCipherCoreWrapper<Rc4Core<U32>>;
|
type Rc4U32 = StreamCipherCoreWrapper<Rc4Core<U32>>;
|
||||||
|
|
||||||
pub fn generate_secure_encryption_pairs(mut session_key: [u8; 32], count: u8) -> Vec<EncryptionPair<Rc4<U32>>>{
|
pub fn generate_secure_encryption_pairs(
|
||||||
|
mut session_key: [u8; 32],
|
||||||
|
count: u8,
|
||||||
|
) -> Vec<EncryptionPair<Rc4<U32>>> {
|
||||||
let mut vec = Vec::with_capacity(count as usize);
|
let mut vec = Vec::with_capacity(count as usize);
|
||||||
|
|
||||||
vec.push(EncryptionPair{
|
vec.push(EncryptionPair {
|
||||||
send: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"),
|
send: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"),
|
||||||
recv: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4")
|
recv: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"),
|
||||||
});
|
});
|
||||||
|
|
||||||
for _ in 1..=count{
|
for _ in 1..=count {
|
||||||
let modifier = session_key.len() + 1;
|
let modifier = session_key.len() + 1;
|
||||||
|
|
||||||
let key_length = session_key.len();
|
let key_length = session_key.len();
|
||||||
|
|
||||||
for (position, val) in (&mut session_key[0..key_length/2]).iter_mut().enumerate(){
|
for (position, val) in (&mut session_key[0..key_length / 2]).iter_mut().enumerate() {
|
||||||
*val = val.wrapping_add((modifier - position) as u8);
|
*val = val.wrapping_add((modifier - position) as u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec.push(EncryptionPair{
|
vec.push(EncryptionPair {
|
||||||
send: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"),
|
send: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"),
|
||||||
recv: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4")
|
recv: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
vec
|
vec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct Secure(pub &'static str, pub Account);
|
pub struct Secure(pub &'static str, pub Account);
|
||||||
|
|
||||||
|
|
||||||
pub struct SecureInstance {
|
pub struct SecureInstance {
|
||||||
access_key: &'static str,
|
access_key: &'static str,
|
||||||
session_key: [u8; 32],
|
session_key: [u8; 32],
|
||||||
|
|
@ -155,18 +97,17 @@ impl CryptoHandler for Secure {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl CryptoHandlerConnectionInstance for SecureInstance {
|
impl CryptoHandlerConnectionInstance for SecureInstance {
|
||||||
type Encryption = Rc4<U5>;
|
type Encryption = Rc4<U5>;
|
||||||
|
|
||||||
fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]) {
|
fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]) {
|
||||||
if let Some(crypt_pair) = self.streams.get_mut(substream as usize){
|
if let Some(crypt_pair) = self.streams.get_mut(substream as usize) {
|
||||||
crypt_pair.recv.apply_keystream(data);
|
crypt_pair.recv.apply_keystream(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]) {
|
fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]) {
|
||||||
if let Some(crypt_pair) = self.streams.get_mut(substream as usize){
|
if let Some(crypt_pair) = self.streams.get_mut(substream as usize) {
|
||||||
crypt_pair.send.apply_keystream(data);
|
crypt_pair.send.apply_keystream(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -182,10 +123,14 @@ impl CryptoHandlerConnectionInstance for SecureInstance {
|
||||||
|
|
||||||
fn sign_packet(&self, packet: &mut PRUDPV1Packet) {
|
fn sign_packet(&self, packet: &mut PRUDPV1Packet) {
|
||||||
packet.set_sizes();
|
packet.set_sizes();
|
||||||
packet.calculate_and_assign_signature(self.access_key, Some(self.session_key), Some(self.self_signature));
|
packet.calculate_and_assign_signature(
|
||||||
|
self.access_key,
|
||||||
|
Some(self.session_key),
|
||||||
|
Some(self.self_signature),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_packet(&self, _packet: &PRUDPV1Packet) -> bool {
|
fn verify_packet(&self, _packet: &PRUDPV1Packet) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,43 +1,34 @@
|
||||||
use crate::prudp::packet::flags::{ACK, HAS_SIZE, MULTI_ACK, NEED_ACK, RELIABLE};
|
|
||||||
use crate::prudp::packet::types::{CONNECT, DATA, DISCONNECT, PING, SYN};
|
|
||||||
use crate::prudp::packet::PacketOption::{
|
use crate::prudp::packet::PacketOption::{
|
||||||
ConnectionSignature, FragmentId, MaximumSubstreamId, SupportedFunctions,
|
ConnectionSignature, FragmentId, MaximumSubstreamId, SupportedFunctions,
|
||||||
};
|
};
|
||||||
use crate::prudp::packet::{PRUDPV1Header, PRUDPV1Packet, TypesFlags, VirtualPort};
|
use crate::prudp::packet::{PRUDPV1Header, PRUDPV1Packet};
|
||||||
use crate::prudp::sockaddr::PRUDPSockAddr;
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use log::info;
|
|
||||||
use log::error;
|
use log::error;
|
||||||
|
use log::{info, warn};
|
||||||
use rc4::StreamCipher;
|
use rc4::StreamCipher;
|
||||||
|
use rnex_core::prudp::socket_addr::PRUDPSockAddr;
|
||||||
|
use rnex_core::prudp::types_flags::TypesFlags;
|
||||||
|
use rnex_core::prudp::types_flags::flags::{ACK, HAS_SIZE, MULTI_ACK, NEED_ACK, RELIABLE};
|
||||||
|
use rnex_core::prudp::types_flags::types::{CONNECT, DATA, DISCONNECT, PING, SYN};
|
||||||
|
use rnex_core::prudp::virtual_port::VirtualPort;
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
|
use std::io::Cursor;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
use v_byte_helpers::ReadExtensions;
|
||||||
|
use v_byte_helpers::little_endian::read_u16;
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::net::UdpSocket;
|
use tokio::net::UdpSocket;
|
||||||
use tokio::sync::mpsc::{channel, Receiver, Sender};
|
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tokio::time::{sleep, Instant};
|
use tokio::sync::mpsc::{Receiver, Sender, channel};
|
||||||
|
use tokio::time::{Instant, sleep};
|
||||||
// due to the way this is designed crashing the router thread causes deadlock, sorry ;-;
|
// due to the way this is designed crashing the router thread causes deadlock, sorry ;-;
|
||||||
// (maybe i will fix that some day)
|
// (maybe i will fix that some day)
|
||||||
|
|
||||||
/// PRUDP Socket for accepting connections to then send and recieve data from those clients
|
/// PRUDP Socket for accepting connections to then send and recieve data from those clients
|
||||||
|
|
||||||
pub struct EncryptionPair<T: StreamCipher + Send> {
|
|
||||||
pub send: T,
|
|
||||||
pub recv: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: StreamCipher + Send> EncryptionPair<T> {
|
|
||||||
pub fn init_both<F: Fn() -> T>(func: F) -> Self {
|
|
||||||
Self {
|
|
||||||
recv: func(),
|
|
||||||
send: func(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CommonConnection {
|
pub struct CommonConnection {
|
||||||
pub user_id: u32,
|
pub user_id: u32,
|
||||||
pub socket_addr: PRUDPSockAddr,
|
pub socket_addr: PRUDPSockAddr,
|
||||||
|
|
@ -50,12 +41,14 @@ struct InternalConnection<E: CryptoHandlerConnectionInstance> {
|
||||||
connections: Weak<Mutex<BTreeMap<PRUDPSockAddr, Arc<Mutex<InternalConnection<E>>>>>>,
|
connections: Weak<Mutex<BTreeMap<PRUDPSockAddr, Arc<Mutex<InternalConnection<E>>>>>>,
|
||||||
reliable_server_counter: u16,
|
reliable_server_counter: u16,
|
||||||
reliable_client_counter: u16,
|
reliable_client_counter: u16,
|
||||||
|
supported_function_version: u32,
|
||||||
// maybe add connection id(need to see if its even needed)
|
// maybe add connection id(need to see if its even needed)
|
||||||
crypto_handler_instance: E,
|
crypto_handler_instance: E,
|
||||||
data_sender: Sender<Vec<u8>>,
|
data_sender: Sender<Vec<u8>>,
|
||||||
socket: Arc<UdpSocket>,
|
socket: Arc<UdpSocket>,
|
||||||
packet_queue: HashMap<u16, PRUDPV1Packet>,
|
packet_queue: HashMap<u16, PRUDPV1Packet>,
|
||||||
last_packet_time: Instant,
|
last_packet_time: Instant,
|
||||||
|
unacknowleged_packets: Vec<(Instant, PRUDPV1Packet)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: CryptoHandlerConnectionInstance> Deref for InternalConnection<E> {
|
impl<E: CryptoHandlerConnectionInstance> Deref for InternalConnection<E> {
|
||||||
|
|
@ -66,6 +59,7 @@ impl<E: CryptoHandlerConnectionInstance> Deref for InternalConnection<E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: CryptoHandlerConnectionInstance> InternalConnection<E> {
|
impl<E: CryptoHandlerConnectionInstance> InternalConnection<E> {
|
||||||
|
/// gives back the next server packet sequence id which the client expects to send, incrementing it in the process
|
||||||
fn next_server_count(&mut self) -> u16 {
|
fn next_server_count(&mut self) -> u16 {
|
||||||
let prev_val = self.reliable_server_counter;
|
let prev_val = self.reliable_server_counter;
|
||||||
let (val, _) = self.reliable_server_counter.overflowing_add(1);
|
let (val, _) = self.reliable_server_counter.overflowing_add(1);
|
||||||
|
|
@ -74,20 +68,30 @@ impl<E: CryptoHandlerConnectionInstance> InternalConnection<E> {
|
||||||
prev_val
|
prev_val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sends a raw packet to a given client on the connection
|
||||||
|
///
|
||||||
|
/// a raw packet is one which does not get processed any further(other than to send it
|
||||||
|
/// off without buffering or anything),
|
||||||
|
/// as such you need to make sure that
|
||||||
|
/// the sizes are set correctly and so on
|
||||||
#[inline]
|
#[inline]
|
||||||
async fn send_raw_packet(&self, mut prudp_packet: PRUDPV1Packet) {
|
async fn send_raw_packet(&self, prudp_packet: &PRUDPV1Packet) {
|
||||||
prudp_packet.set_sizes();
|
send_raw_prudp_to_sockaddr(&self.socket, self.socket_addr, prudp_packet).await;
|
||||||
|
}
|
||||||
|
|
||||||
let mut vec = Vec::new();
|
async fn delete_connection(&self) {
|
||||||
|
let Some(conns) = self.connections.upgrade() else {
|
||||||
|
// this is fine as it implies the server has already quit, thus meaning that we dont
|
||||||
|
// have to remove ourselves from the server
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
prudp_packet
|
let mut conns = conns.lock().await;
|
||||||
.write_to(&mut vec)
|
|
||||||
.expect("somehow failed to convert backet to bytes");
|
|
||||||
|
|
||||||
self.socket
|
conns.remove(&self.socket_addr);
|
||||||
.send_to(&vec, self.socket_addr.regular_socket_addr)
|
|
||||||
.await
|
// the connection will now drop as soon as we leave this due to no longer having a permanent
|
||||||
.expect("failed to send data back");
|
// reference
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -195,7 +199,9 @@ impl<T: CryptoHandlerConnectionInstance> AnyInternalConnection for InternalConne
|
||||||
|
|
||||||
self.crypto_handler_instance.sign_packet(&mut packet);
|
self.crypto_handler_instance.sign_packet(&mut packet);
|
||||||
|
|
||||||
self.send_raw_packet(packet).await;
|
self.send_raw_packet(&packet).await;
|
||||||
|
|
||||||
|
self.unacknowleged_packets.push((Instant::now(), packet));
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn close_connection(&mut self) {
|
async fn close_connection(&mut self) {
|
||||||
|
|
@ -222,23 +228,29 @@ impl<T: CryptoHandlerConnectionInstance> AnyInternalConnection for InternalConne
|
||||||
|
|
||||||
self.crypto_handler_instance.sign_packet(&mut packet);
|
self.crypto_handler_instance.sign_packet(&mut packet);
|
||||||
|
|
||||||
self.send_raw_packet(packet).await;
|
self.send_raw_packet(&packet).await;
|
||||||
|
|
||||||
let Some(conns) = self.connections.upgrade() else {
|
self.delete_connection().await;
|
||||||
// this is fine as it implies the server has already quit, thus meaning that we dont
|
|
||||||
// have to remove ourselves from the server
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut conns = conns.lock().await;
|
|
||||||
|
|
||||||
conns.remove(&self.socket_addr);
|
|
||||||
|
|
||||||
// the connection will now drop as soon as we leave this due to no longer having a permanent
|
|
||||||
// reference
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn send_raw_prudp_to_sockaddr(
|
||||||
|
udp_socket: &UdpSocket,
|
||||||
|
dest: PRUDPSockAddr,
|
||||||
|
packet: &PRUDPV1Packet,
|
||||||
|
) {
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
|
||||||
|
packet
|
||||||
|
.write_to(&mut vec)
|
||||||
|
.expect("somehow failed to convert backet to bytes");
|
||||||
|
|
||||||
|
udp_socket
|
||||||
|
.send_to(&vec, dest.regular_socket_addr)
|
||||||
|
.await
|
||||||
|
.expect("failed to send data back");
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: CryptoHandler> InternalSocket<T> {
|
impl<T: CryptoHandler> InternalSocket<T> {
|
||||||
async fn get_connection(
|
async fn get_connection(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -256,19 +268,12 @@ impl<T: CryptoHandler> InternalSocket<T> {
|
||||||
Some(conn)
|
Some(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_packet_unbuffered(&self, dest: PRUDPSockAddr, mut packet: PRUDPV1Packet) {
|
/// sends a raw packet to a specific prudp socket address
|
||||||
packet.set_sizes();
|
///
|
||||||
|
/// a raw packet is a packet is a packet which wont get processed any further,
|
||||||
let mut vec = Vec::new();
|
/// sizes signatures etc need to be set before using this function
|
||||||
|
async fn send_packet_unbuffered(&self, dest: PRUDPSockAddr, packet: &PRUDPV1Packet) {
|
||||||
packet
|
send_raw_prudp_to_sockaddr(&self.socket, dest, packet).await
|
||||||
.write_to(&mut vec)
|
|
||||||
.expect("somehow failed to convert backet to bytes");
|
|
||||||
|
|
||||||
self.socket
|
|
||||||
.send_to(&vec, dest.regular_socket_addr)
|
|
||||||
.await
|
|
||||||
.expect("failed to send data back");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_syn(&self, address: PRUDPSockAddr, packet: PRUDPV1Packet) {
|
async fn handle_syn(&self, address: PRUDPSockAddr, packet: PRUDPV1Packet) {
|
||||||
|
|
@ -303,7 +308,7 @@ impl<T: CryptoHandler> InternalSocket<T> {
|
||||||
|
|
||||||
//println!("got syn: {:?}", response);
|
//println!("got syn: {:?}", response);
|
||||||
|
|
||||||
self.send_packet_unbuffered(address, response).await;
|
self.send_packet_unbuffered(address, &response).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn connection_thread(
|
async fn connection_thread(
|
||||||
|
|
@ -315,7 +320,7 @@ impl<T: CryptoHandler> InternalSocket<T> {
|
||||||
let mut conn = conn.lock().await;
|
let mut conn = conn.lock().await;
|
||||||
|
|
||||||
if conn.last_packet_time < (Instant::now() - Duration::from_secs(5)) {
|
if conn.last_packet_time < (Instant::now() - Duration::from_secs(5)) {
|
||||||
conn.send_raw_packet(PRUDPV1Packet {
|
conn.send_raw_packet(&PRUDPV1Packet {
|
||||||
header: PRUDPV1Header {
|
header: PRUDPV1Header {
|
||||||
sequence_id: 0,
|
sequence_id: 0,
|
||||||
substream_id: 0,
|
substream_id: 0,
|
||||||
|
|
@ -335,9 +340,24 @@ impl<T: CryptoHandler> InternalSocket<T> {
|
||||||
if conn.last_packet_time < (Instant::now() - Duration::from_secs(30)) {
|
if conn.last_packet_time < (Instant::now() - Duration::from_secs(30)) {
|
||||||
conn.close_connection().await;
|
conn.close_connection().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (send_time, packet) in &conn.unacknowleged_packets {
|
||||||
|
if *send_time < (Instant::now() - Duration::from_millis(3000)) {
|
||||||
|
warn!(
|
||||||
|
"failed to resend packet 5 times and never got response, destroying connection"
|
||||||
|
);
|
||||||
|
conn.close_connection().await;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if *send_time < (Instant::now() - Duration::from_millis(500)) {
|
||||||
|
info!("unacknowledged packet sat arround for more than 500 ms, resending");
|
||||||
|
conn.send_raw_packet(packet).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
drop(conn);
|
drop(conn);
|
||||||
|
|
||||||
sleep(Duration::from_secs(5)).await;
|
sleep(Duration::from_millis(500)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -347,6 +367,7 @@ impl<T: CryptoHandler> InternalSocket<T> {
|
||||||
socket_addr: PRUDPSockAddr,
|
socket_addr: PRUDPSockAddr,
|
||||||
session_id: u8,
|
session_id: u8,
|
||||||
is_instantiator: bool,
|
is_instantiator: bool,
|
||||||
|
supported_function_version: u32,
|
||||||
) {
|
) {
|
||||||
let common = Arc::new(CommonConnection {
|
let common = Arc::new(CommonConnection {
|
||||||
user_id: crypto_handler_instance.get_user_id(),
|
user_id: crypto_handler_instance.get_user_id(),
|
||||||
|
|
@ -367,6 +388,8 @@ impl<T: CryptoHandler> InternalSocket<T> {
|
||||||
socket: self.socket.clone(),
|
socket: self.socket.clone(),
|
||||||
packet_queue: Default::default(),
|
packet_queue: Default::default(),
|
||||||
last_packet_time: Instant::now(),
|
last_packet_time: Instant::now(),
|
||||||
|
unacknowleged_packets: Vec::new(),
|
||||||
|
supported_function_version,
|
||||||
};
|
};
|
||||||
|
|
||||||
let internal = Arc::new(Mutex::new(internal));
|
let internal = Arc::new(Mutex::new(internal));
|
||||||
|
|
@ -444,12 +467,17 @@ impl<T: CryptoHandler> InternalSocket<T> {
|
||||||
.options
|
.options
|
||||||
.push(ConnectionSignature(Default::default()));
|
.push(ConnectionSignature(Default::default()));
|
||||||
|
|
||||||
|
let mut functions: u32 = 0;
|
||||||
|
|
||||||
for option in &packet.options {
|
for option in &packet.options {
|
||||||
match option {
|
match option {
|
||||||
MaximumSubstreamId(max_substream) => {
|
MaximumSubstreamId(max_substream) => {
|
||||||
response.options.push(MaximumSubstreamId(*max_substream))
|
response.options.push(MaximumSubstreamId(*max_substream))
|
||||||
}
|
}
|
||||||
SupportedFunctions(funcs) => response.options.push(SupportedFunctions(*funcs & 0xFF)),
|
SupportedFunctions(funcs) => {
|
||||||
|
functions = *funcs & 0xFF;
|
||||||
|
response.options.push(SupportedFunctions(*funcs & 0xFF));
|
||||||
|
}
|
||||||
_ => { /* ? */ }
|
_ => { /* ? */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -460,10 +488,10 @@ impl<T: CryptoHandler> InternalSocket<T> {
|
||||||
|
|
||||||
//println!("connect out: {:?}", response);
|
//println!("connect out: {:?}", response);
|
||||||
|
|
||||||
self.create_connection(crypto, address, session_id, false)
|
self.create_connection(crypto, address, session_id, false, functions)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
self.send_packet_unbuffered(address, response).await;
|
self.send_packet_unbuffered(address, &response).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_data(&self, address: PRUDPSockAddr, packet: PRUDPV1Packet) {
|
async fn handle_data(&self, address: PRUDPSockAddr, packet: PRUDPV1Packet) {
|
||||||
|
|
@ -502,7 +530,7 @@ impl<T: CryptoHandler> InternalSocket<T> {
|
||||||
|
|
||||||
conn.crypto_handler_instance.sign_packet(&mut response);
|
conn.crypto_handler_instance.sign_packet(&mut response);
|
||||||
|
|
||||||
self.send_packet_unbuffered(address, response).await;
|
self.send_packet_unbuffered(address, &response).await;
|
||||||
|
|
||||||
conn.data_sender.send(packet.payload).await.ok();
|
conn.data_sender.send(packet.payload).await.ok();
|
||||||
|
|
||||||
|
|
@ -528,7 +556,7 @@ impl<T: CryptoHandler> InternalSocket<T> {
|
||||||
|
|
||||||
conn.crypto_handler_instance.sign_packet(&mut response);
|
conn.crypto_handler_instance.sign_packet(&mut response);
|
||||||
|
|
||||||
self.send_packet_unbuffered(address, response).await;
|
self.send_packet_unbuffered(address, &response).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_disconnect(&self, address: PRUDPSockAddr, packet: PRUDPV1Packet) {
|
async fn handle_disconnect(&self, address: PRUDPSockAddr, packet: PRUDPV1Packet) {
|
||||||
|
|
@ -548,10 +576,11 @@ impl<T: CryptoHandler> InternalSocket<T> {
|
||||||
|
|
||||||
conn.crypto_handler_instance.sign_packet(&mut response);
|
conn.crypto_handler_instance.sign_packet(&mut response);
|
||||||
|
|
||||||
self.send_packet_unbuffered(address, response.clone()).await;
|
self.send_packet_unbuffered(address, &response).await;
|
||||||
self.send_packet_unbuffered(address, response.clone()).await;
|
self.send_packet_unbuffered(address, &response).await;
|
||||||
self.send_packet_unbuffered(address, response).await;
|
self.send_packet_unbuffered(address, &response).await;
|
||||||
|
|
||||||
|
conn.delete_connection().await;
|
||||||
//self.internal_connections.lock().await;
|
//self.internal_connections.lock().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -571,7 +600,6 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
|
||||||
if (packet.header.types_and_flags.get_flags() & ACK) != 0 {
|
if (packet.header.types_and_flags.get_flags() & ACK) != 0 {
|
||||||
info!("got ack");
|
info!("got ack");
|
||||||
|
|
||||||
|
|
||||||
if packet.header.types_and_flags.get_types() == SYN
|
if packet.header.types_and_flags.get_types() == SYN
|
||||||
|| packet.header.types_and_flags.get_types() == CONNECT
|
|| packet.header.types_and_flags.get_types() == CONNECT
|
||||||
{
|
{
|
||||||
|
|
@ -596,13 +624,76 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
|
||||||
} else {
|
} else {
|
||||||
error!("got connection response without the active reciever being present");
|
error!("got connection response without the active reciever being present");
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("got ack");
|
||||||
|
|
||||||
|
if let Some(conn) = self.get_connection(address).await {
|
||||||
|
let mut conn = conn.lock().await;
|
||||||
|
|
||||||
|
// remove the packet whose sequence id matches the ack packet
|
||||||
|
// or in other words keep all of those which dont match the sequence id
|
||||||
|
conn.unacknowleged_packets
|
||||||
|
.retain_mut(|v| packet.header.sequence_id != v.1.header.sequence_id);
|
||||||
|
} else {
|
||||||
|
error!("non connection acknowledgement packet on nonexistent connection...")
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (packet.header.types_and_flags.get_flags() & MULTI_ACK) != 0 {
|
if (packet.header.types_and_flags.get_flags() & MULTI_ACK) != 0 {
|
||||||
info!("got multi ack");
|
if let Some(conn) = self.get_connection(address).await {
|
||||||
|
let mut conn = conn.lock().await;
|
||||||
|
|
||||||
|
if conn.supported_function_version == 1 {
|
||||||
|
let mut collected_ids: Vec<u16> = Vec::new();
|
||||||
|
let mut cursor = Cursor::new(&packet.payload);
|
||||||
|
|
||||||
|
while let Ok(v) = read_u16(&mut cursor) {
|
||||||
|
collected_ids.push(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.unacknowleged_packets.retain_mut(|(_, up)| {
|
||||||
|
!(collected_ids.iter().any(|id| up.header.sequence_id == *id)
|
||||||
|
|| up.header.sequence_id <= packet.header.sequence_id)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let mut collected_ids: Vec<u16> = Vec::new();
|
||||||
|
let mut cursor = Cursor::new(&packet.payload);
|
||||||
|
|
||||||
|
let Ok(_substream_id): Result<u8, _> = cursor.read_le_struct() else {
|
||||||
|
error!("invalid data whilest reading new version agregate acknowledgement");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Ok(additional_sequence_ids): Result<u8, _> = cursor.read_le_struct() else {
|
||||||
|
error!("invalid data whilest reading new version agregate acknowledgement");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Ok(sequence_id): Result<u16, _> = cursor.read_le_struct() else {
|
||||||
|
error!("invalid data whilest reading new version agregate acknowledgement");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
for _ in 0..additional_sequence_ids {
|
||||||
|
let Ok(additional_sequence_id): Result<u16, _> = cursor.read_le_struct()
|
||||||
|
else {
|
||||||
|
error!(
|
||||||
|
"invalid data whilest reading new version agregate acknowledgement"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
collected_ids.push(additional_sequence_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.unacknowleged_packets.retain_mut(|(_, up)| {
|
||||||
|
!(collected_ids.iter().any(|id| up.header.sequence_id == *id)
|
||||||
|
|| up.header.sequence_id <= sequence_id)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!("non connection acknowledgement packet on nonexistent connection...")
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -630,7 +721,7 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
|
||||||
|
|
||||||
let remote_signature = address.calculate_connection_signature();
|
let remote_signature = address.calculate_connection_signature();
|
||||||
|
|
||||||
let packet = PRUDPV1Packet {
|
let mut packet = PRUDPV1Packet {
|
||||||
header: PRUDPV1Header {
|
header: PRUDPV1Header {
|
||||||
source_port: self.virtual_port,
|
source_port: self.virtual_port,
|
||||||
destination_port: address.virtual_port,
|
destination_port: address.virtual_port,
|
||||||
|
|
@ -645,7 +736,9 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
self.send_packet_unbuffered(address, packet).await;
|
packet.set_sizes();
|
||||||
|
|
||||||
|
self.send_packet_unbuffered(address, &packet).await;
|
||||||
|
|
||||||
let Some(syn_ack_packet) = recv.recv().await else {
|
let Some(syn_ack_packet) = recv.recv().await else {
|
||||||
error!("what");
|
error!("what");
|
||||||
|
|
@ -661,7 +754,7 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
let packet = PRUDPV1Packet {
|
let mut packet = PRUDPV1Packet {
|
||||||
header: PRUDPV1Header {
|
header: PRUDPV1Header {
|
||||||
source_port: self.virtual_port,
|
source_port: self.virtual_port,
|
||||||
destination_port: address.virtual_port,
|
destination_port: address.virtual_port,
|
||||||
|
|
@ -676,9 +769,11 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
self.send_packet_unbuffered(address, packet).await;
|
packet.set_sizes();
|
||||||
|
|
||||||
let Some(connect_ack_packet) = recv.recv().await else {
|
self.send_packet_unbuffered(address, &packet).await;
|
||||||
|
|
||||||
|
let Some(_connect_ack_packet) = recv.recv().await else {
|
||||||
error!("what");
|
error!("what");
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
@ -688,7 +783,7 @@ impl<T: CryptoHandler> AnyInternalSocket for InternalSocket<T> {
|
||||||
.instantiate(remote_signature, *own_signature, &[], 1)?;
|
.instantiate(remote_signature, *own_signature, &[], 1)?;
|
||||||
|
|
||||||
//todo: make this work for secure servers as well
|
//todo: make this work for secure servers as well
|
||||||
self.create_connection(crypt, address, 0, true).await;
|
self.create_connection(crypt, address, 0, true, 4).await;
|
||||||
|
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
|
use crate::prudp::packet::PRUDPV1Packet;
|
||||||
|
use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use rc4::{Key, KeyInit, Rc4, StreamCipher};
|
use rc4::{Key, KeyInit, Rc4, StreamCipher};
|
||||||
|
use rnex_core::prudp::encryption::{DEFAULT_KEY, EncryptionPair};
|
||||||
use typenum::U5;
|
use typenum::U5;
|
||||||
use crate::prudp::packet::PRUDPV1Packet;
|
|
||||||
use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance, EncryptionPair};
|
|
||||||
|
|
||||||
pub struct Unsecure(pub &'static str);
|
pub struct Unsecure(pub &'static str);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub struct UnsecureInstance {
|
pub struct UnsecureInstance {
|
||||||
key: &'static str,
|
key: &'static str,
|
||||||
streams: Vec<EncryptionPair<Rc4<U5>>>,
|
streams: Vec<EncryptionPair<Rc4<U5>>>,
|
||||||
|
|
@ -18,7 +17,6 @@ pub struct UnsecureInstance {
|
||||||
// my hand was forced to use lazy so that we can guarantee this code
|
// my hand was forced to use lazy so that we can guarantee this code
|
||||||
// only runs once and so that i can put it here as a "constant" (for performance and readability)
|
// only runs once and so that i can put it here as a "constant" (for performance and readability)
|
||||||
// since for some reason rust crypto doesn't have any const time key initialization
|
// since for some reason rust crypto doesn't have any const time key initialization
|
||||||
static DEFAULT_KEY: Lazy<Key<U5>> = Lazy::new(|| Key::from(*b"CD&ML"));
|
|
||||||
|
|
||||||
impl CryptoHandler for Unsecure {
|
impl CryptoHandler for Unsecure {
|
||||||
type CryptoConnectionInstance = UnsecureInstance;
|
type CryptoConnectionInstance = UnsecureInstance;
|
||||||
|
|
@ -53,13 +51,13 @@ impl CryptoHandlerConnectionInstance for UnsecureInstance {
|
||||||
type Encryption = Rc4<U5>;
|
type Encryption = Rc4<U5>;
|
||||||
|
|
||||||
fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]) {
|
fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]) {
|
||||||
if let Some(crypt_pair) = self.streams.get_mut(substream as usize){
|
if let Some(crypt_pair) = self.streams.get_mut(substream as usize) {
|
||||||
crypt_pair.recv.apply_keystream(data);
|
crypt_pair.recv.apply_keystream(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]) {
|
fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]) {
|
||||||
if let Some(crypt_pair) = self.streams.get_mut(substream as usize){
|
if let Some(crypt_pair) = self.streams.get_mut(substream as usize) {
|
||||||
crypt_pair.send.apply_keystream(data);
|
crypt_pair.send.apply_keystream(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -78,7 +76,7 @@ impl CryptoHandlerConnectionInstance for UnsecureInstance {
|
||||||
packet.calculate_and_assign_signature(self.key, None, Some(self.self_signature));
|
packet.calculate_and_assign_signature(self.key, None, Some(self.self_signature));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_packet(&self, packet: &PRUDPV1Packet) -> bool {
|
fn verify_packet(&self, _packet: &PRUDPV1Packet) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
60
rnex-core/Cargo.toml
Normal file
60
rnex-core/Cargo.toml
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
[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 = ["full"] }
|
||||||
|
hex = "0.4.3"
|
||||||
|
|
||||||
|
macros = { path = "../macros" }
|
||||||
|
paste = "1.0.15"
|
||||||
|
typenum = "1.18.0"
|
||||||
|
json = "0.12.4"
|
||||||
|
anyhow = "1.0.100"
|
||||||
|
ureq = { version = "3.1.4", features = [ "json" ] }
|
||||||
|
serde = { version = "1.0.228", features = [ "derive" ] }
|
||||||
|
serde_json = "1.0.149"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
# criterion = "0.7.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
rmc_struct_header = []
|
||||||
|
guest_login = []
|
||||||
|
friends = ["guest_login"]
|
||||||
|
big_pid = []
|
||||||
|
v3-8-15 = ["rmc_struct_header"]
|
||||||
|
v4-3-11 = ["v3-8-15"]
|
||||||
|
nx = ["big_pid"]
|
||||||
|
|
||||||
|
|
||||||
|
[[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;
|
||||||
|
}
|
||||||
24
rnex-core/src/executables/backend_server_secure.rs
Normal file
24
rnex-core/src/executables/backend_server_secure.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
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 {
|
||||||
|
use rnex_core::executables::regular_backend;
|
||||||
|
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::io::Cursor;
|
||||||
use std::net::SocketAddrV4;
|
use std::net::SocketAddrV4;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use log::error;
|
|
||||||
use macros::rmc_struct;
|
use macros::rmc_struct;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use rust_nex::common::setup;
|
use rnex_core::common::setup;
|
||||||
use rust_nex::executables::common::{OWN_IP_PRIVATE, SERVER_PORT};
|
use rnex_core::executables::common::{OWN_IP_PRIVATE, SERVER_PORT};
|
||||||
use rust_nex::reggie::{EdgeNodeHolderConnectOption, EdgeNodeManagement, LocalEdgeNodeHolder};
|
use rnex_core::reggie::{EdgeNodeHolderConnectOption, EdgeNodeManagement, LocalEdgeNodeHolder};
|
||||||
use rust_nex::rmc::protocols::new_rmc_gateway_connection;
|
use rnex_core::rmc::protocols::new_rmc_gateway_connection;
|
||||||
use rust_nex::rmc::response::ErrorCode;
|
use rnex_core::rmc::response::ErrorCode;
|
||||||
use rust_nex::util::SplittableBufferConnection;
|
use rnex_core::util::SplittableBufferConnection;
|
||||||
use rust_nex::rmc::structures::RmcSerialize;
|
use rnex_core::rmc::structures::RmcSerialize;
|
||||||
|
|
||||||
#[rmc_struct(EdgeNodeHolder)]
|
#[rmc_struct(EdgeNodeHolder)]
|
||||||
struct EdgeNode{
|
struct EdgeNode{
|
||||||
|
|
@ -56,7 +55,7 @@ async fn main() {
|
||||||
|
|
||||||
let holder: Arc<DataHolder> = Default::default();
|
let holder: Arc<DataHolder> = Default::default();
|
||||||
|
|
||||||
while let Ok((mut stream, addr)) = listen.accept().await {
|
while let Ok((stream, _addr)) = listen.accept().await {
|
||||||
let mut conn: SplittableBufferConnection = stream.into();
|
let mut conn: SplittableBufferConnection = stream.into();
|
||||||
|
|
||||||
let Some(data) = conn.recv().await else {
|
let Some(data) = conn.recv().await else {
|
||||||
76
rnex-core/src/executables/friends_backend.rs
Normal file
76
rnex-core/src/executables/friends_backend.rs
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
use std::{
|
||||||
|
io::Cursor,
|
||||||
|
net::SocketAddrV4,
|
||||||
|
sync::{Arc, atomic::AtomicU32},
|
||||||
|
};
|
||||||
|
|
||||||
|
use log::error;
|
||||||
|
use tokio::net::TcpListener;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
executables::common::{OWN_IP_PRIVATE, SERVER_PORT, new_simple_backend},
|
||||||
|
nex::friends_handler::{
|
||||||
|
FriendsGuest, FriendsManager, FriendsUser, RemoteFriendRemote, RemoteFriendsUser,
|
||||||
|
},
|
||||||
|
reggie::UnitPacketRead,
|
||||||
|
rmc::{
|
||||||
|
protocols::{
|
||||||
|
RmcCallable, RmcPureRemoteObject, friends::RemoteFriends, new_rmc_gateway_connection,
|
||||||
|
},
|
||||||
|
structures::RmcSerialize,
|
||||||
|
},
|
||||||
|
rnex_proxy_common::ConnectionInitData,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn start_friends_backend() {
|
||||||
|
let fm = Arc::new(FriendsManager {
|
||||||
|
cid_counter: AtomicU32::new(1),
|
||||||
|
users: Default::default(),
|
||||||
|
});
|
||||||
|
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 c = match user_connection_data {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
error!("an error ocurred whilest reading connection data: {:?}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let fm = fm.clone();
|
||||||
|
if c.pid != 100 {
|
||||||
|
new_rmc_gateway_connection(stream.into(), move |r| {
|
||||||
|
Arc::new_cyclic(move |this| FriendsUser {
|
||||||
|
fm,
|
||||||
|
addr: c.prudpsock_addr,
|
||||||
|
pid: c.pid,
|
||||||
|
data: Default::default(),
|
||||||
|
current_friends: Default::default(),
|
||||||
|
this: this.clone(),
|
||||||
|
remote: RemoteFriendRemote::new(r),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
new_rmc_gateway_connection(stream.into(), move |r| {
|
||||||
|
Arc::new_cyclic(move |this| FriendsGuest {
|
||||||
|
fm,
|
||||||
|
addr: c.prudpsock_addr,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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,52 +1,60 @@
|
||||||
use std::{env, result};
|
|
||||||
use std::array::TryFromSliceError;
|
|
||||||
use std::str::FromStr;
|
|
||||||
use json::{object, JsonValue};
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use reqwest::{Body, Method, Url};
|
|
||||||
use reqwest::header::HeaderValue;
|
|
||||||
use thiserror::Error;
|
|
||||||
use crate::grpc::account::Error::SomethingHappened;
|
use crate::grpc::account::Error::SomethingHappened;
|
||||||
static API_KEY: Lazy<String> = Lazy::new(||{
|
use json::{JsonValue, object};
|
||||||
let key = env::var("ACCOUNT_GQL_API_KEY")
|
use once_cell::sync::Lazy;
|
||||||
.expect("no graphql ip specified");
|
use rnex_core::PID;
|
||||||
|
use std::array::TryFromSliceError;
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::{env, result};
|
||||||
|
use thiserror::Error;
|
||||||
|
use tokio::task::{JoinError, spawn_blocking};
|
||||||
|
static API_KEY: Lazy<String> = Lazy::new(|| {
|
||||||
|
let key = env::var("ACCOUNT_GQL_API_KEY").expect("no graphql ip specified");
|
||||||
|
|
||||||
key
|
key
|
||||||
});
|
});
|
||||||
|
|
||||||
static CLIENT_URI: Lazy<String> = Lazy::new(||{
|
static CLIENT_URI: Lazy<String> = Lazy::new(|| {
|
||||||
env::var("ACCOUNT_GQL_URL")
|
env::var("ACCOUNT_GQL_URL")
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|s| s.parse().ok())
|
.and_then(|s| s.parse().ok())
|
||||||
.expect("no graphql ip specified")
|
.expect("no graphql ip specified")
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error{
|
pub enum Error {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Creation(#[from] reqwest::Error),
|
RequestError(#[from] ureq::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Json(#[from] json::Error),
|
Json(#[from] json::Error),
|
||||||
#[error(transparent)]
|
//#[error(transparent)]
|
||||||
Status(#[from] tonic::Status),
|
//Status(#[from] tonic::Status),
|
||||||
#[error("invalid password size: {0}")]
|
#[error("invalid password size: {0}")]
|
||||||
PasswordConversion(#[from] TryFromSliceError),
|
PasswordConversion(#[from] TryFromSliceError),
|
||||||
#[error("something happened")]
|
#[error("something happened")]
|
||||||
SomethingHappened
|
SomethingHappened,
|
||||||
|
#[error("error joining blocking task: {0}")]
|
||||||
|
Join(#[from] JoinError),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = result::Result<T, Error>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
pub struct Client(reqwest::Client);
|
pub struct Client; //(reqwest::Client);
|
||||||
|
|
||||||
impl Client{
|
impl Client {
|
||||||
pub async fn new() -> Result<Self> {
|
pub async fn new() -> Result<Self> {
|
||||||
Ok(Self(reqwest::ClientBuilder::new().build()?))
|
//Ok(Self(reqwest::ClientBuilder::new().build()?))
|
||||||
|
Ok(Self)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn do_request(&self, request_data: JsonValue) -> Result<JsonValue>{
|
async fn do_request(&self, request_data: JsonValue) -> Result<JsonValue> {
|
||||||
|
let request = ureq::post(CLIENT_URI.as_str())
|
||||||
|
.header("X-API-Key", API_KEY.deref())
|
||||||
|
.content_type("application/json");
|
||||||
|
let mut response = spawn_blocking(move || request.send(request_data.to_string())).await??;
|
||||||
|
|
||||||
|
let str_body = response.body_mut().read_to_string()?;
|
||||||
|
Ok(json::parse(&str_body)?)
|
||||||
|
/*
|
||||||
let mut request = reqwest::Request::new(Method::POST, Url::from_str(CLIENT_URI.as_str()).unwrap());
|
let mut request = reqwest::Request::new(Method::POST, Url::from_str(CLIENT_URI.as_str()).unwrap());
|
||||||
|
|
||||||
*(request.body_mut()) = Some(Body::from(request_data.to_string()));
|
*(request.body_mut()) = Some(Body::from(request_data.to_string()));
|
||||||
|
|
@ -56,36 +64,81 @@ impl Client{
|
||||||
let response = self.0.execute(request).await?;
|
let response = self.0.execute(request).await?;
|
||||||
|
|
||||||
Ok(json::parse(&response.text().await?)?)
|
Ok(json::parse(&response.text().await?)?)
|
||||||
|
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_nex_password(&mut self , pid: u32) -> Result<[u8; 16]>{
|
pub async fn get_nex_password(&mut self, pid: PID) -> Result<[u8; 16]> {
|
||||||
let req = self.do_request(object!{
|
let req = self
|
||||||
"query": r"query($pid: Int!){
|
.do_request(object! {
|
||||||
|
"query": r"query($pid: Int!){
|
||||||
userByPid(pid: $pid){
|
userByPid(pid: $pid){
|
||||||
nexPassword
|
nexPassword
|
||||||
}
|
}
|
||||||
}",
|
}",
|
||||||
"variables": {
|
"variables": {
|
||||||
"pid": pid
|
"pid": pid
|
||||||
}
|
}
|
||||||
}).await?;
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
let Some(val) = req.entries()
|
let Some(val) = req
|
||||||
|
.entries()
|
||||||
.find(|v| v.0 == "data")
|
.find(|v| v.0 == "data")
|
||||||
.ok_or(SomethingHappened)?.1
|
.ok_or(SomethingHappened)?
|
||||||
|
.1
|
||||||
.entries()
|
.entries()
|
||||||
.find(|v| v.0 == "userByPid")
|
.find(|v| v.0 == "userByPid")
|
||||||
.ok_or(SomethingHappened)?.1
|
.ok_or(SomethingHappened)?
|
||||||
|
.1
|
||||||
.entries()
|
.entries()
|
||||||
.find(|v| v.0 == "nexPassword")
|
.find(|v| v.0 == "nexPassword")
|
||||||
.ok_or(SomethingHappened)?.1
|
.ok_or(SomethingHappened)?
|
||||||
.as_str() else {
|
.1
|
||||||
|
.as_str()
|
||||||
|
else {
|
||||||
return Err(SomethingHappened);
|
return Err(SomethingHappened);
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(val.as_bytes().try_into().map_err(|_| SomethingHappened)?)
|
Ok(val.as_bytes().try_into().map_err(|_| SomethingHappened)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_pid_from_token(&mut self, token: String) -> Result<PID> {
|
||||||
|
let req = self
|
||||||
|
.do_request(object! {
|
||||||
|
"query":
|
||||||
|
r"query($token: String!){
|
||||||
|
token(tokenData: $token){
|
||||||
|
pid
|
||||||
|
}
|
||||||
|
}",
|
||||||
|
"variables": {
|
||||||
|
"token": token
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
// this breaks switch nex servers and should be fixed eventually
|
||||||
|
let Some(val) = req
|
||||||
|
.entries()
|
||||||
|
.find(|v| v.0 == "data")
|
||||||
|
.ok_or(SomethingHappened)?
|
||||||
|
.1
|
||||||
|
.entries()
|
||||||
|
.find(|v| v.0 == "token")
|
||||||
|
.ok_or(SomethingHappened)?
|
||||||
|
.1
|
||||||
|
.entries()
|
||||||
|
.find(|v| v.0 == "pid")
|
||||||
|
.ok_or(SomethingHappened)?
|
||||||
|
.1
|
||||||
|
.as_u32()
|
||||||
|
else {
|
||||||
|
return Err(SomethingHappened);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(val)
|
||||||
|
}
|
||||||
|
|
||||||
/*pub async fn get_user_data(&mut self , pid: u32) -> Result<GetUserDataResponse>{
|
/*pub async fn get_user_data(&mut self , pid: u32) -> Result<GetUserDataResponse>{
|
||||||
let req = Request::new(GetUserDataRequest{
|
let req = Request::new(GetUserDataRequest{
|
||||||
pid
|
pid
|
||||||
|
|
@ -97,8 +150,6 @@ impl Client{
|
||||||
}*/
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
pub struct Client(AccountClient<InterceptedService<Channel, InterceptorFunc>>);
|
pub struct Client(AccountClient<InterceptedService<Channel, InterceptorFunc>>);
|
||||||
|
|
@ -137,20 +188,3 @@ impl Client{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
#[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.
|
//! before account rs is finished.
|
||||||
//!
|
//!
|
||||||
//! This WILL be deprecated as soon as account rs is in a stable state.
|
//! This WILL be deprecated as soon as account rs is in a stable state.
|
||||||
use tonic::{Request, Status};
|
//use tonic::{Request, Status};
|
||||||
|
|
||||||
type InterceptorFunc = Box<(dyn Fn(Request<()>) -> Result<Request<()>, Status> + Send)>;
|
//type InterceptorFunc = Box<dyn Fn(Request<()>) -> Result<Request<()>, Status> + Send>;
|
||||||
mod protobufs;
|
|
||||||
pub mod account;
|
pub mod account;
|
||||||
206
rnex-core/src/kerberos/mod.rs
Normal file
206
rnex-core/src/kerberos/mod.rs
Normal file
|
|
@ -0,0 +1,206 @@
|
||||||
|
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::KeyInit;
|
||||||
|
use rc4::cipher::StreamCipherCoreWrapper;
|
||||||
|
use rc4::{Rc4, Rc4Core, StreamCipher};
|
||||||
|
use rnex_core::rmc::structures::RmcSerialize;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use typenum::U16;
|
||||||
|
use typenum::Unsigned;
|
||||||
|
|
||||||
|
use rnex_core::rmc::structures::Result;
|
||||||
|
|
||||||
|
use rnex_core::PID;
|
||||||
|
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(feature = "friends")]{
|
||||||
|
pub type SESSION_KEY_LENGTH_TY = U16;
|
||||||
|
} else {
|
||||||
|
use rc4::consts::U32;
|
||||||
|
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: PID, 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: [u8; 16] = {
|
||||||
|
let mut md5 = Md5::new();
|
||||||
|
md5.update(password);
|
||||||
|
md5.finalize().try_into().unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
for _ in 1..iteration_count {
|
||||||
|
let mut md5 = Md5::new();
|
||||||
|
md5.update(key);
|
||||||
|
key = md5.finalize().try_into().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
key
|
||||||
|
}
|
||||||
|
#[derive(Pod, Zeroable, Copy, Clone, Debug, Eq, PartialEq, Default)]
|
||||||
|
#[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 {
|
||||||
|
Self(second | (minute << 6) | (hour << 12) | (day << 17) | (month << 22) | (year << 26))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn now() -> Self {
|
||||||
|
let now = chrono::Utc::now();
|
||||||
|
Self::new(
|
||||||
|
now.second() as u64,
|
||||||
|
now.minute() as u64,
|
||||||
|
now.hour() as u64,
|
||||||
|
now.day() as u64,
|
||||||
|
now.month() as u64,
|
||||||
|
now.year() as u64,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_seconds(&self) -> u8 {
|
||||||
|
(self.0 & 0b111111) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_minutes(&self) -> u8 {
|
||||||
|
((self.0 >> 6) & 0b111111) as u8
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn get_hours(&self) -> u8 {
|
||||||
|
((self.0 >> 12) & 0b111111) as u8
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn get_days(&self) -> u8 {
|
||||||
|
((self.0 >> 17) & 0b111111) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_month(&self) -> u8 {
|
||||||
|
((self.0 >> 22) & 0b1111) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_year(&self) -> u64 {
|
||||||
|
(self.0 >> 26) & 0xFFFFFFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RmcSerialize for KerberosDateTime {
|
||||||
|
fn serialize(&self, writer: &mut impl Write) -> Result<()> {
|
||||||
|
Ok(self.0.serialize(writer)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 issued_time: KerberosDateTime,
|
||||||
|
pub pid: PID,
|
||||||
|
pub session_key: [u8; SESSION_KEY_LENGTH],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TicketInternalData {
|
||||||
|
pub(crate) fn new(pid: PID) -> Self {
|
||||||
|
Self {
|
||||||
|
issued_time: KerberosDateTime::now(),
|
||||||
|
pid,
|
||||||
|
session_key: rand::random(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn encrypt(&self, key: [u8; 16]) -> Box<[u8]> {
|
||||||
|
let mut data = bytes_of(self).to_vec();
|
||||||
|
|
||||||
|
let mut rc4: StreamCipherCoreWrapper<Rc4Core<U16>> = Rc4::new_from_slice(&key).unwrap();
|
||||||
|
rc4.apply_keystream(&mut data);
|
||||||
|
|
||||||
|
let mut hmac = <Md5Hmac as KeyInit>::new_from_slice(&key).unwrap();
|
||||||
|
|
||||||
|
hmac.write_all(&data[..])
|
||||||
|
.expect("failed to write data to hmac");
|
||||||
|
|
||||||
|
let hmac_result = &hmac.finalize().into_bytes()[..];
|
||||||
|
|
||||||
|
data.write_all(&hmac_result)
|
||||||
|
.expect("failed to write data to vec");
|
||||||
|
|
||||||
|
data.into_boxed_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Pod, Zeroable, Debug, Copy, Clone)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct Ticket {
|
||||||
|
pub session_key: [u8; SESSION_KEY_LENGTH],
|
||||||
|
pub pid: PID,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ticket {
|
||||||
|
pub fn encrypt(&self, key: [u8; 16], internal_data: &[u8]) -> Box<[u8]> {
|
||||||
|
let mut data = bytes_of(self).to_vec();
|
||||||
|
|
||||||
|
internal_data
|
||||||
|
.serialize(&mut data)
|
||||||
|
.expect("unable to write to vec");
|
||||||
|
|
||||||
|
let mut rc4: StreamCipherCoreWrapper<Rc4Core<U16>> = Rc4::new_from_slice(&key).unwrap();
|
||||||
|
rc4.apply_keystream(&mut data);
|
||||||
|
|
||||||
|
let mut hmac = <Md5Hmac as KeyInit>::new_from_slice(&key).unwrap();
|
||||||
|
|
||||||
|
hmac.write_all(&data[..])
|
||||||
|
.expect("failed to write data to hmac");
|
||||||
|
|
||||||
|
let hmac_result = &hmac.finalize().into_bytes()[..];
|
||||||
|
|
||||||
|
data.write_all(&hmac_result)
|
||||||
|
.expect("failed to write data to vec");
|
||||||
|
|
||||||
|
data.into_boxed_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::kerberos::KerberosDateTime;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn kerberos_time_convert_test() {
|
||||||
|
let time = KerberosDateTime(135904948834);
|
||||||
|
|
||||||
|
println!("{}", time.to_regular_time().to_rfc2822());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,23 +4,29 @@
|
||||||
#![allow(async_fn_in_trait)]
|
#![allow(async_fn_in_trait)]
|
||||||
//#![warn(missing_docs)]
|
//#![warn(missing_docs)]
|
||||||
|
|
||||||
|
#[cfg(feature = "big_pid")]
|
||||||
|
pub type PID = u64;
|
||||||
|
#[cfg(not(feature = "big_pid"))]
|
||||||
|
pub type PID = u32;
|
||||||
|
|
||||||
|
extern crate self as rnex_core;
|
||||||
|
|
||||||
extern crate self as rust_nex;
|
|
||||||
|
|
||||||
pub mod endianness;
|
|
||||||
pub mod prudp;
|
pub mod prudp;
|
||||||
pub mod rmc;
|
pub mod rmc;
|
||||||
//mod protocols;
|
//mod protocols;
|
||||||
|
|
||||||
|
pub mod common;
|
||||||
|
pub mod executables;
|
||||||
pub mod grpc;
|
pub mod grpc;
|
||||||
pub mod kerberos;
|
pub mod kerberos;
|
||||||
pub mod nex;
|
pub mod nex;
|
||||||
pub mod result;
|
|
||||||
pub mod versions;
|
|
||||||
pub mod web;
|
|
||||||
pub mod common;
|
|
||||||
pub mod reggie;
|
pub mod reggie;
|
||||||
|
pub mod result;
|
||||||
pub mod rnex_proxy_common;
|
pub mod rnex_proxy_common;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod executables;
|
pub mod versions;
|
||||||
|
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;
|
||||||
|
|
||||||
|
use rnex_core::PID;
|
||||||
|
|
||||||
|
#[derive(RmcSerialize, Clone)]
|
||||||
|
pub struct Account {
|
||||||
|
pub pid: PID,
|
||||||
|
pub username: String,
|
||||||
|
pub kerbros_password: Box<[u8]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Account {
|
||||||
|
pub fn new(pid: PID, username: &str, passwd: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
kerbros_password: passwd.as_bytes().into(),
|
||||||
|
username: username.into(),
|
||||||
|
pid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_raw_password(pid: PID, username: &str, passwd: &[u8]) -> Self {
|
||||||
|
Self {
|
||||||
|
kerbros_password: passwd.into(),
|
||||||
|
username: username.into(),
|
||||||
|
pid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_login_data(&self) -> (PID, &[u8]) {
|
||||||
|
(self.pid, &self.kerbros_password)
|
||||||
|
}
|
||||||
|
}
|
||||||
418
rnex-core/src/nex/auth_handler.rs
Normal file
418
rnex-core/src/nex/auth_handler.rs
Normal file
|
|
@ -0,0 +1,418 @@
|
||||||
|
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::PID;
|
||||||
|
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, LazyLock};
|
||||||
|
|
||||||
|
define_rmc_proto!(
|
||||||
|
proto AuthClientProtocol{
|
||||||
|
Auth
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#[rmc_struct(AuthClientProtocol)]
|
||||||
|
pub struct AuthHandler {
|
||||||
|
pub destination_server_acct: &'static Account,
|
||||||
|
pub build_name: &'static str,
|
||||||
|
//pub station_url: &'static str,
|
||||||
|
pub control_server: Arc<OnlyRemote<RemoteEdgeNodeHolder>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_ticket(
|
||||||
|
source_act_login_data: (PID, &[u8]),
|
||||||
|
dest_act_login_data: (PID, &[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);
|
||||||
|
|
||||||
|
let internal_data = kerberos::TicketInternalData::new(source_act_login_data.0);
|
||||||
|
|
||||||
|
let encrypted_inner = internal_data.encrypt(dest_key);
|
||||||
|
let encrypted_session_ticket = Ticket {
|
||||||
|
pid: dest_act_login_data.0,
|
||||||
|
session_key: internal_data.session_key,
|
||||||
|
}
|
||||||
|
.encrypt(source_key, &encrypted_inner);
|
||||||
|
|
||||||
|
encrypted_session_ticket
|
||||||
|
}
|
||||||
|
pub fn generate_ticket_with_string_user_key(
|
||||||
|
source_act: PID,
|
||||||
|
dest_act_login_data: (PID, &[u8]),
|
||||||
|
) -> (String, Box<[u8]>) {
|
||||||
|
let source_key: [u8; 8] = rand::random();
|
||||||
|
let key_string = hex::encode(source_key);
|
||||||
|
let key_data: [u8; 16] = key_string.as_bytes().try_into().unwrap();
|
||||||
|
let dest_key = derive_key(dest_act_login_data.0, dest_act_login_data.1);
|
||||||
|
|
||||||
|
let internal_data = kerberos::TicketInternalData::new(source_act);
|
||||||
|
|
||||||
|
let encrypted_inner = internal_data.encrypt(dest_key);
|
||||||
|
let encrypted_session_ticket = Ticket {
|
||||||
|
pid: dest_act_login_data.0,
|
||||||
|
session_key: internal_data.session_key,
|
||||||
|
}
|
||||||
|
.encrypt(key_data, &encrypted_inner);
|
||||||
|
|
||||||
|
(key_string, encrypted_session_ticket)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_login_data_by_pid(pid: PID) -> Option<(PID, 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(passwd) = client.get_nex_password(pid).await else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
Some((pid, passwd.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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<(PID, 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[..]);
|
||||||
|
println!("{}, {:?}", pid, passwd);
|
||||||
|
let destination_login_data = self.destination_server_acct.get_login_data();
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
pid,
|
||||||
|
generate_ticket(source_login_data, destination_login_data),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn generate_ticket_from_name_string_user_key(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<(PID, String, Box<[u8]>), ErrorCode> {
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
let ticket = generate_ticket_with_string_user_key(
|
||||||
|
source_login_data.0,
|
||||||
|
destination_login_data,
|
||||||
|
);
|
||||||
|
|
||||||
|
return Ok((source_login_data.0, ticket.0, ticket.1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let Ok(pid) = name.parse() else {
|
||||||
|
warn!("unable to connect to parse pid: {}", name);
|
||||||
|
return Err(ErrorCode::Core_InvalidArgument);
|
||||||
|
};
|
||||||
|
let destination_login_data = self.destination_server_acct.get_login_data();
|
||||||
|
|
||||||
|
let data = generate_ticket_with_string_user_key(pid, destination_login_data);
|
||||||
|
Ok((pid, data.0, data.1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Auth for AuthHandler {
|
||||||
|
async fn login(
|
||||||
|
&self,
|
||||||
|
name: String,
|
||||||
|
) -> Result<(QResult, PID, 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)
|
||||||
|
}
|
||||||
|
cfg_if! {
|
||||||
|
|
||||||
|
if #[cfg(feature = "nx")]{
|
||||||
|
async fn login_ex(
|
||||||
|
&self,
|
||||||
|
name: String,
|
||||||
|
_extra_data: Any,
|
||||||
|
) -> Result<(QResult, PID, Vec<u8>, ConnectionData, String, String), ErrorCode> {
|
||||||
|
let (pid, key, ticket) = self.generate_ticket_from_name_string_user_key(&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 = ConnectionData {
|
||||||
|
station_url: station_url_from_sock_addr(addr),
|
||||||
|
special_station_url: "".to_string(),
|
||||||
|
//date_time: KerberosDateTime::new(1,1,1,1,1,1),
|
||||||
|
date_time: KerberosDateTime::now(),
|
||||||
|
special_protocols: Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ret = (
|
||||||
|
result,
|
||||||
|
pid,
|
||||||
|
ticket.into(),
|
||||||
|
connection_data,
|
||||||
|
self.build_name.to_string(),
|
||||||
|
key
|
||||||
|
);
|
||||||
|
|
||||||
|
info!("data: {:?}", ret);
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
async fn request_ticket(
|
||||||
|
&self,
|
||||||
|
source_pid: PID,
|
||||||
|
destination_pid: PID,
|
||||||
|
) -> Result<(QResult, Vec<u8>, String), ErrorCode> {
|
||||||
|
let Some((pid, _)) = 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 {
|
||||||
|
return Err(ErrorCode::RendezVous_InvalidOperation);
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = QResult::success(Core_Unknown);
|
||||||
|
|
||||||
|
let ticket = generate_ticket_with_string_user_key(pid, desgination_login_data);
|
||||||
|
|
||||||
|
Ok((result, ticket.1.into(), ticket.0))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
async fn login_ex(
|
||||||
|
&self,
|
||||||
|
name: String,
|
||||||
|
_extra_data: Any,
|
||||||
|
) -> Result<(QResult, PID, Vec<u8>, ConnectionData, 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 = ConnectionData {
|
||||||
|
station_url: station_url_from_sock_addr(addr),
|
||||||
|
special_station_url: "".to_string(),
|
||||||
|
//date_time: KerberosDateTime::new(1,1,1,1,1,1),
|
||||||
|
date_time: KerberosDateTime::now(),
|
||||||
|
special_protocols: Vec::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ret = (
|
||||||
|
result,
|
||||||
|
pid,
|
||||||
|
ticket.into(),
|
||||||
|
connection_data,
|
||||||
|
self.build_name.to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
info!("data: {:?}", ret);
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
async fn request_ticket(
|
||||||
|
&self,
|
||||||
|
source_pid: PID,
|
||||||
|
destination_pid: PID,
|
||||||
|
) -> Result<(QResult, Vec<u8>), ErrorCode> {
|
||||||
|
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 {
|
||||||
|
return Err(ErrorCode::RendezVous_InvalidOperation);
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = QResult::success(Core_Unknown);
|
||||||
|
|
||||||
|
let ticket = generate_ticket((pid, &passwd[..]), desgination_login_data);
|
||||||
|
|
||||||
|
Ok((result, ticket.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_pid(&self, _username: String) -> Result<u32, ErrorCode> {
|
||||||
|
Err(ErrorCode::Core_Exception)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_name(&self, _pid: PID) -> Result<String, ErrorCode> {
|
||||||
|
Err(ErrorCode::Core_Exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod test {
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
use rc4::{KeyInit, Rc4, StreamCipher};
|
||||||
|
use rnex_core::PID;
|
||||||
|
use rnex_core::kerberos::KerberosDateTime;
|
||||||
|
use rnex_core::rmc::structures::connection_data::ConnectionData;
|
||||||
|
use rnex_core::rmc::{
|
||||||
|
response::ErrorCode,
|
||||||
|
structures::{RmcSerialize, qresult::QResult},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::kerberos::{self, derive_key};
|
||||||
|
use crate::rmc;
|
||||||
|
use crate::rmc::message::RMCMessage;
|
||||||
|
use crate::rmc::response::{RMCResponse, RMCResponseResult};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
return;
|
||||||
|
let packet = [
|
||||||
|
26, 1, 0, 0, 10, 1, 30, 0, 0, 0, 1, 128, 0, 0, 1, 0, 1, 0, 86, 4, 0, 0, 116, 0, 0, 0,
|
||||||
|
144, 209, 130, 175, 45, 215, 95, 55, 226, 192, 51, 54, 201, 84, 118, 150, 159, 164, 32,
|
||||||
|
103, 134, 252, 199, 168, 178, 5, 6, 208, 206, 241, 94, 23, 136, 37, 109, 247, 156, 252,
|
||||||
|
189, 233, 142, 115, 206, 72, 180, 57, 106, 223, 37, 59, 144, 208, 250, 197, 51, 202,
|
||||||
|
185, 156, 51, 159, 219, 117, 250, 103, 184, 1, 103, 108, 15, 14, 174, 160, 192, 146,
|
||||||
|
135, 10, 55, 125, 68, 181, 88, 127, 183, 34, 4, 213, 19, 146, 81, 56, 248, 213, 241,
|
||||||
|
168, 205, 253, 29, 10, 123, 198, 177, 157, 247, 209, 113, 167, 231, 42, 214, 15, 12,
|
||||||
|
200, 192, 230, 125, 227, 74, 0, 112, 114, 117, 100, 112, 115, 58, 47, 80, 73, 68, 61,
|
||||||
|
50, 59, 115, 105, 100, 61, 49, 59, 115, 116, 114, 101, 97, 109, 61, 49, 48, 59, 116,
|
||||||
|
121, 112, 101, 61, 50, 59, 97, 100, 100, 114, 101, 115, 115, 61, 57, 49, 46, 57, 56,
|
||||||
|
46, 49, 50, 56, 46, 56, 54, 59, 112, 111, 114, 116, 61, 54, 48, 48, 49, 59, 67, 73, 68,
|
||||||
|
61, 49, 0, 0, 0, 0, 0, 1, 0, 0, 162, 243, 240, 168, 31, 0, 0, 0, 51, 0, 98, 114, 97,
|
||||||
|
110, 99, 104, 58, 111, 114, 105, 103, 105, 110, 47, 112, 114, 111, 106, 101, 99, 116,
|
||||||
|
47, 119, 117, 112, 45, 97, 103, 109, 106, 32, 98, 117, 105, 108, 100, 58, 51, 95, 56,
|
||||||
|
95, 49, 53, 95, 50, 48, 48, 52, 95, 48, 0,
|
||||||
|
];
|
||||||
|
let rmc_packet = RMCResponse::new(&mut Cursor::new(&packet)).unwrap();
|
||||||
|
println!("{:?}", rmc_packet);
|
||||||
|
|
||||||
|
let RMCResponseResult::Success {
|
||||||
|
call_id,
|
||||||
|
method_id,
|
||||||
|
data,
|
||||||
|
} = rmc_packet.response_result
|
||||||
|
else {
|
||||||
|
panic!();
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("{}", hex::encode(&data));
|
||||||
|
|
||||||
|
let mut data =
|
||||||
|
<(QResult, PID, Vec<u8>, ConnectionData, String) as RmcSerialize>::deserialize(
|
||||||
|
&mut Cursor::new(&data[..]),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!("{:?}", data);
|
||||||
|
|
||||||
|
let key = derive_key(1110, "AAAAAAAAAAAAAAAA".as_bytes());
|
||||||
|
|
||||||
|
let mut rc4 = Rc4::new((&key).into());
|
||||||
|
|
||||||
|
rc4.apply_keystream(&mut data.2);
|
||||||
|
println!("raw tick: {:?}", data.2);
|
||||||
|
|
||||||
|
let tick: &kerberos::Ticket =
|
||||||
|
bytemuck::from_bytes(&data.2[..size_of::<kerberos::Ticket>()]);
|
||||||
|
|
||||||
|
let remainder = &data.2[size_of::<kerberos::Ticket>()..];
|
||||||
|
|
||||||
|
println!("tick: {:?}", tick);
|
||||||
|
let data = <Vec<u8> as RmcSerialize>::deserialize(&mut Cursor::new(remainder)).unwrap();
|
||||||
|
println!("inner ticket raw: {:?}", data);
|
||||||
|
|
||||||
|
println!("{:?}", data);
|
||||||
|
}
|
||||||
|
}
|
||||||
95
rnex-core/src/nex/common.rs
Normal file
95
rnex-core/src/nex/common.rs
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
use rnex_core::PID;
|
||||||
|
|
||||||
|
pub async fn get_station_urls(
|
||||||
|
station_urls: &[StationUrl],
|
||||||
|
addr: PRUDPSockAddr,
|
||||||
|
pid: PID,
|
||||||
|
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().clone()));
|
||||||
|
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])
|
||||||
|
}
|
||||||
389
rnex-core/src/nex/friends_handler.rs
Normal file
389
rnex-core/src/nex/friends_handler.rs
Normal file
|
|
@ -0,0 +1,389 @@
|
||||||
|
use std::io::{Cursor, Write};
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::sync::Weak;
|
||||||
|
use std::sync::{Arc, atomic::AtomicU32};
|
||||||
|
|
||||||
|
use bytemuck::bytes_of;
|
||||||
|
use hmac::Mac;
|
||||||
|
use log::info;
|
||||||
|
use macros::rmc_struct;
|
||||||
|
use rnex_core::rmc::protocols::account_management::{
|
||||||
|
AccountManagement, RawAccountManagement, RawAccountManagementInfo, RemoteAccountManagement,
|
||||||
|
};
|
||||||
|
use rnex_core::rmc::protocols::friends::{Friends, RawFriends, RawFriendsInfo, RemoteFriends};
|
||||||
|
use rnex_core::rmc::protocols::nintendo_notification::{
|
||||||
|
NintendoNotification, RawNintendoNotification, RawNintendoNotificationInfo,
|
||||||
|
RemoteNintendoNotification,
|
||||||
|
};
|
||||||
|
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::spawn;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
|
use rnex_core::rmc::protocols::friends::{GameKey, MiiV2, PrincipalBasicInfo};
|
||||||
|
|
||||||
|
use rnex_core::PID;
|
||||||
|
|
||||||
|
use rnex_core::rmc::protocols::account_management::NintendoCreateAccountData;
|
||||||
|
use rnex_core::rmc::protocols::nintendo_notification::NintendoNotificationEvent;
|
||||||
|
use rnex_core::rmc::structures::RmcSerialize;
|
||||||
|
|
||||||
|
use rnex_core::rmc::structures::data::Data;
|
||||||
|
|
||||||
|
define_rmc_proto!(
|
||||||
|
proto FriendsUser{
|
||||||
|
Secure,
|
||||||
|
Friends
|
||||||
|
}
|
||||||
|
);
|
||||||
|
define_rmc_proto!(
|
||||||
|
proto FriendRemote{
|
||||||
|
NintendoNotification
|
||||||
|
}
|
||||||
|
);
|
||||||
|
define_rmc_proto!(
|
||||||
|
proto FriendsGuest{
|
||||||
|
Secure,
|
||||||
|
AccountManagement
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
pub struct UserData {
|
||||||
|
info: NNAInfo,
|
||||||
|
presence: NintendoPresenceV2,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rmc_struct(FriendsUser)]
|
||||||
|
pub struct FriendsUser {
|
||||||
|
pub fm: Arc<FriendsManager>,
|
||||||
|
pub addr: PRUDPSockAddr,
|
||||||
|
pub pid: PID,
|
||||||
|
pub data: RwLock<Option<UserData>>,
|
||||||
|
pub current_friends: RwLock<Vec<PID>>,
|
||||||
|
pub this: Weak<FriendsUser>,
|
||||||
|
pub remote: RemoteFriendRemote,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rmc_struct(FriendsGuest)]
|
||||||
|
pub struct FriendsGuest {
|
||||||
|
pub fm: Arc<FriendsManager>,
|
||||||
|
pub addr: PRUDPSockAddr,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FriendsManager {
|
||||||
|
pub cid_counter: AtomicU32,
|
||||||
|
pub users: RwLock<Vec<Weak<FriendsUser>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FriendsManager {
|
||||||
|
pub fn next_cid(&self) -> u32 {
|
||||||
|
self.cid_counter.fetch_add(1, Relaxed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn friend_info_from_user(data: &UserData) -> FriendInfo {
|
||||||
|
FriendInfo {
|
||||||
|
data: Data {},
|
||||||
|
nna_info: data.info.clone(),
|
||||||
|
presence: data.presence.clone(),
|
||||||
|
comment: Comment {
|
||||||
|
data: Data {},
|
||||||
|
unk: 0,
|
||||||
|
message: "haii =w=".to_string(),
|
||||||
|
last_changed: KerberosDateTime::now(),
|
||||||
|
},
|
||||||
|
became_friends: KerberosDateTime::now(),
|
||||||
|
last_online: KerberosDateTime::now(),
|
||||||
|
unk: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
> {
|
||||||
|
println!("updating own data");
|
||||||
|
let mut data = self.data.write().await;
|
||||||
|
*data = Some(UserData { info, presence });
|
||||||
|
let self_fr_info = friend_info_from_user(data.as_ref().unwrap());
|
||||||
|
let Ok(any_self_fr_info) = Any::new(&self_fr_info) else {
|
||||||
|
return Err(ErrorCode::RendezVous_ControlScriptFailure);
|
||||||
|
};
|
||||||
|
drop(data);
|
||||||
|
|
||||||
|
let mut fr_list = vec![FriendInfo {
|
||||||
|
data: Data{},
|
||||||
|
became_friends: KerberosDateTime::now(),
|
||||||
|
comment: Comment {
|
||||||
|
data: Data{},
|
||||||
|
last_changed: KerberosDateTime::now(),
|
||||||
|
message: "I'm just a dummy account :3".to_string(),
|
||||||
|
unk: 0,
|
||||||
|
},
|
||||||
|
last_online: KerberosDateTime::now(),
|
||||||
|
nna_info: NNAInfo {
|
||||||
|
data: Data{},
|
||||||
|
principal_basic_info: PrincipalBasicInfo {
|
||||||
|
data: Data{},
|
||||||
|
pid: 101,
|
||||||
|
nnid: "dummy:3".to_string(),
|
||||||
|
mii: MiiV2{
|
||||||
|
data: Data{},
|
||||||
|
date_time: KerberosDateTime::now(),
|
||||||
|
name: "TheDummy".to_string(),
|
||||||
|
mii_data: hex::decode("030000402bd7c32986a771f2dc6b35e31da15e37ff7c0000391e6f006f006d0069000000000000000000000000004040001065033568641e2013661a611821640f0000290052485000000000000000000000000000000000000000000000e838").unwrap(),
|
||||||
|
unk: 0,
|
||||||
|
unk2: 0,
|
||||||
|
},
|
||||||
|
unk: 0
|
||||||
|
},
|
||||||
|
unk: 0,
|
||||||
|
unk2: 0
|
||||||
|
},
|
||||||
|
presence: NintendoPresenceV2{
|
||||||
|
data: Data{},
|
||||||
|
changed_flags: 0,
|
||||||
|
message: "".to_string(),
|
||||||
|
app_data: vec![],
|
||||||
|
game_key: GameKey{
|
||||||
|
data: Data{},
|
||||||
|
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
|
||||||
|
}];
|
||||||
|
|
||||||
|
println!("acquiring user and current friends locks");
|
||||||
|
let users = self.fm.users.read().await;
|
||||||
|
println!("started summing users");
|
||||||
|
for u in users.deref().iter().filter_map(|u| u.upgrade()) {
|
||||||
|
let data = u.data.read().await;
|
||||||
|
let Some(inner_data) = data.as_ref() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
fr_list.push(friend_info_from_user(&inner_data));
|
||||||
|
drop(data);
|
||||||
|
|
||||||
|
let mut curr_friends = self.current_friends.write().await;
|
||||||
|
curr_friends.push(u.pid);
|
||||||
|
drop(curr_friends);
|
||||||
|
|
||||||
|
let mut fr = u.current_friends.write().await;
|
||||||
|
if !fr.contains(&self.pid) {
|
||||||
|
fr.push(self.pid);
|
||||||
|
drop(fr);
|
||||||
|
let data = any_self_fr_info.clone();
|
||||||
|
let u = u.clone();
|
||||||
|
let sender = self.pid;
|
||||||
|
spawn(async move {
|
||||||
|
u.remote
|
||||||
|
.process_nintendo_notification_event_1(NintendoNotificationEvent {
|
||||||
|
event_type: 30,
|
||||||
|
sender,
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
drop(fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("finished summing users");
|
||||||
|
drop(users);
|
||||||
|
|
||||||
|
println!("adding self to users");
|
||||||
|
let mut users = self.fm.users.write().await;
|
||||||
|
users.push(self.this.clone());
|
||||||
|
drop(users);
|
||||||
|
|
||||||
|
println!("done...");
|
||||||
|
Ok((
|
||||||
|
PrincipalPreference {
|
||||||
|
data: Data {},
|
||||||
|
block_friend_request: false,
|
||||||
|
show_online: false,
|
||||||
|
show_playing_title: false,
|
||||||
|
},
|
||||||
|
Comment {
|
||||||
|
data: Data {},
|
||||||
|
last_changed: KerberosDateTime::now(),
|
||||||
|
message: "".to_string(),
|
||||||
|
unk: 0,
|
||||||
|
},
|
||||||
|
fr_list,
|
||||||
|
vec![],
|
||||||
|
vec![],
|
||||||
|
vec![],
|
||||||
|
false,
|
||||||
|
vec![],
|
||||||
|
false,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_presence(&self, presence: NintendoPresenceV2) -> Result<(), ErrorCode> {
|
||||||
|
let mut data = self.data.write().await;
|
||||||
|
let Some(inner_data) = data.as_mut() else {
|
||||||
|
return Err(ErrorCode::RendezVous_PermissionDenied);
|
||||||
|
};
|
||||||
|
inner_data.presence = presence;
|
||||||
|
let Ok(any_self_fr_info) = Any::new(&inner_data.presence) else {
|
||||||
|
return Err(ErrorCode::RendezVous_ControlScriptFailure);
|
||||||
|
};
|
||||||
|
drop(data);
|
||||||
|
|
||||||
|
let users = self.fm.users.read().await;
|
||||||
|
for u in users.deref().iter().filter_map(|u| u.upgrade()) {
|
||||||
|
u.remote
|
||||||
|
.process_nintendo_notification_event_2(NintendoNotificationEvent {
|
||||||
|
event_type: 24,
|
||||||
|
sender: self.pid,
|
||||||
|
data: any_self_fr_info.clone(),
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
drop(users);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_persistent_notification(
|
||||||
|
&self,
|
||||||
|
notifs: Vec<PersistentNotification>,
|
||||||
|
) -> Result<(), ErrorCode> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn check_setting_status(&self) -> Result<u8, ErrorCode> {
|
||||||
|
Ok(0xFF)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type HMacMd5 = hmac::Hmac<md5::Md5>;
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Secure for FriendsGuest {
|
||||||
|
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, 100, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccountManagement for FriendsGuest {
|
||||||
|
async fn nintendo_create_account(
|
||||||
|
&self,
|
||||||
|
principal_name: String,
|
||||||
|
key: String,
|
||||||
|
groups: u32,
|
||||||
|
email: String,
|
||||||
|
auth_data: Any,
|
||||||
|
) -> Result<(PID, String), ErrorCode> {
|
||||||
|
println!("{}, {}, {}, {}", principal_name, key, groups, email);
|
||||||
|
if auth_data.name == "NintendoCreateAccountData" {
|
||||||
|
let Ok(data) =
|
||||||
|
NintendoCreateAccountData::deserialize(&mut Cursor::new(&auth_data.data))
|
||||||
|
else {
|
||||||
|
return Err(ErrorCode::Authentication_InvalidParam);
|
||||||
|
};
|
||||||
|
|
||||||
|
let pid = data.nna_info.principal_basic_info.pid;
|
||||||
|
info!("create account: {}", pid);
|
||||||
|
|
||||||
|
let Ok(mut mac) = HMacMd5::new_from_slice(key.as_bytes()) else {
|
||||||
|
return Err(ErrorCode::Authentication_InvalidParam);
|
||||||
|
};
|
||||||
|
|
||||||
|
mac.write_all(bytes_of(&pid))
|
||||||
|
.expect("failed to write to hmac???");
|
||||||
|
let mac = mac.finalize().into_bytes();
|
||||||
|
|
||||||
|
let hex_str = hex::encode(mac);
|
||||||
|
|
||||||
|
return Ok((pid, hex_str));
|
||||||
|
}
|
||||||
|
Err(ErrorCode::Core_NotImplemented)
|
||||||
|
}
|
||||||
|
}
|
||||||
485
rnex-core/src/nex/matchmake.rs
Normal file
485
rnex-core/src/nex/matchmake.rs
Normal file
|
|
@ -0,0 +1,485 @@
|
||||||
|
use crate::nex::user::User;
|
||||||
|
use crate::rmc::protocols::notifications::{NotificationEvent, RemoteNotification};
|
||||||
|
use log::info;
|
||||||
|
use rand::random;
|
||||||
|
use rnex_core::PID;
|
||||||
|
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 as PID,
|
||||||
|
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 as PID,
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
/*if old_conns.pid != self.session.gathering.host_pid {
|
||||||
|
continue;
|
||||||
|
}*/
|
||||||
|
for new_conn_pid in conns.iter().filter_map(Weak::upgrade).map(|c| c.pid) {
|
||||||
|
old_conns
|
||||||
|
.remote
|
||||||
|
.process_notification_event(NotificationEvent {
|
||||||
|
pid_source: initiating_pid,
|
||||||
|
notif_type: 3001,
|
||||||
|
param_1: self.session.gathering.self_gid as PID,
|
||||||
|
param_2: new_conn_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: PID) -> 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 as PID,
|
||||||
|
param_2: new_owner.pid,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn migrate_host(&mut self, initiator_pid: PID) -> 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 as PID,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn remove_player_from_session(
|
||||||
|
&mut self,
|
||||||
|
pid: PID,
|
||||||
|
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 as PID,
|
||||||
|
param_2: pid,
|
||||||
|
str_param: message.to_owned(),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
pub mod account;
|
pub mod account;
|
||||||
pub mod auth_handler;
|
pub mod auth_handler;
|
||||||
pub mod user;
|
pub mod common;
|
||||||
|
pub mod friends_handler;
|
||||||
|
pub mod matchmake;
|
||||||
pub mod remote_console;
|
pub mod remote_console;
|
||||||
pub mod matchmake;
|
pub mod user;
|
||||||
737
rnex-core/src/nex/user.rs
Normal file
737
rnex-core/src/nex/user.rs
Normal file
|
|
@ -0,0 +1,737 @@
|
||||||
|
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::rmc::protocols::matchmake::{
|
||||||
|
Matchmake, RawMatchmake, RawMatchmakeInfo, RemoteMatchmake,
|
||||||
|
};
|
||||||
|
use crate::rmc::protocols::nat_traversal::{
|
||||||
|
NatTraversal, RawNatTraversal, RawNatTraversalInfo, RemoteNatTraversal,
|
||||||
|
RemoteNatTraversalConsole,
|
||||||
|
};
|
||||||
|
use rnex_core::PID;
|
||||||
|
use rnex_core::kerberos::KerberosDateTime;
|
||||||
|
use rnex_core::prudp::station_url::StationUrl;
|
||||||
|
use rnex_core::prudp::station_url::UrlOptions::{
|
||||||
|
Address, NatFiltering, NatMapping, Port, RVConnectionID,
|
||||||
|
};
|
||||||
|
use rnex_core::rmc::protocols::matchmake_ext::{
|
||||||
|
MatchmakeExt, RawMatchmakeExt, RawMatchmakeExtInfo, RemoteMatchmakeExt,
|
||||||
|
};
|
||||||
|
use rnex_core::rmc::protocols::matchmake_extension::{
|
||||||
|
MatchmakeExtension, RawMatchmakeExtension, RawMatchmakeExtensionInfo, RemoteMatchmakeExtension,
|
||||||
|
};
|
||||||
|
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 serde::{Deserialize, Serialize};
|
||||||
|
use std::env;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use crate::rmc::protocols::notifications::{NotificationEvent, RemoteNotification};
|
||||||
|
use log::{error, info};
|
||||||
|
use macros::rmc_struct;
|
||||||
|
use rnex_core::prudp::socket_addr::PRUDPSockAddr;
|
||||||
|
use rnex_core::rmc::protocols::ranking::{
|
||||||
|
CompetitionRankingGetParam, CompetitionRankingScoreData, CompetitionRankingScoreInfo,
|
||||||
|
};
|
||||||
|
use rnex_core::rmc::response::ErrorCode::{Core_InvalidArgument, RendezVous_AccountExpired};
|
||||||
|
use rnex_core::rmc::structures::qbuffer::QBuffer;
|
||||||
|
use rnex_core::rmc::structures::qresult::QResult;
|
||||||
|
use rnex_core::rmc::structures::ranking::UploadCompetitionData;
|
||||||
|
use std::sync::{Arc, Weak};
|
||||||
|
use tokio::sync::{Mutex, RwLock};
|
||||||
|
|
||||||
|
define_rmc_proto!(
|
||||||
|
proto UserProtocol{
|
||||||
|
Secure,
|
||||||
|
MatchmakeExtension,
|
||||||
|
MatchmakeExt,
|
||||||
|
Matchmake,
|
||||||
|
NatTraversal,
|
||||||
|
Ranking
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#[rmc_struct(UserProtocol)]
|
||||||
|
pub struct User {
|
||||||
|
pub pid: PID,
|
||||||
|
pub ip: PRUDPSockAddr,
|
||||||
|
pub this: Weak<User>,
|
||||||
|
pub remote: RemoteConsole,
|
||||||
|
pub station_url: RwLock<Vec<StationUrl>>,
|
||||||
|
pub matchmake_manager: Arc<MatchmakeManager>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Secure for User {
|
||||||
|
async fn register(
|
||||||
|
&self,
|
||||||
|
station_urls: Vec<StationUrl>,
|
||||||
|
) -> Result<(QResult, u32, StationUrl), ErrorCode> {
|
||||||
|
let cid = self.matchmake_manager.next_cid();
|
||||||
|
|
||||||
|
println!("{:?}", station_urls);
|
||||||
|
|
||||||
|
let mut users = self.matchmake_manager.users.write().await;
|
||||||
|
users.insert(cid, self.this.clone());
|
||||||
|
drop(users);
|
||||||
|
|
||||||
|
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 = stations;
|
||||||
|
|
||||||
|
drop(lock);
|
||||||
|
|
||||||
|
let result = QResult::success(ErrorCode::Core_Unknown);
|
||||||
|
|
||||||
|
Ok((result, cid, first))
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
||||||
|
let mut lock = self.station_url.write().await;
|
||||||
|
|
||||||
|
let Some(target_addr) = target_url.options.iter().find(|v| matches!(v, Address(_))) else {
|
||||||
|
return Err(ErrorCode::Core_InvalidArgument);
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(target_port) = target_url.options.iter().find(|v| matches!(v, Port(_))) else {
|
||||||
|
return Err(ErrorCode::Core_InvalidArgument);
|
||||||
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
}) else {
|
||||||
|
return Err(ErrorCode::Core_InvalidArgument);
|
||||||
|
};
|
||||||
|
*replacement_target = dest;
|
||||||
|
|
||||||
|
drop(lock);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MatchmakeExtension for User {
|
||||||
|
async fn close_participation(&self, gid: u32) -> Result<(), ErrorCode> {
|
||||||
|
let session = self.matchmake_manager.get_session(gid).await?;
|
||||||
|
|
||||||
|
let mut session = session.lock().await;
|
||||||
|
|
||||||
|
session.session.open_participation = false;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn open_participation(&self, gid: u32) -> Result<(), ErrorCode> {
|
||||||
|
let session = self.matchmake_manager.get_session(gid).await?;
|
||||||
|
|
||||||
|
let mut session = session.lock().await;
|
||||||
|
|
||||||
|
session.session.open_participation = true;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_playing_session(&self, _pids: Vec<u32>) -> Result<Vec<()>, ErrorCode> {
|
||||||
|
Ok(Vec::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_progress_score(&self, gid: u32, progress: u8) -> Result<(), ErrorCode> {
|
||||||
|
let session = self.matchmake_manager.get_session(gid).await?;
|
||||||
|
|
||||||
|
let mut session = session.lock().await;
|
||||||
|
|
||||||
|
session.session.progress_score = progress;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_matchmake_session_with_param(
|
||||||
|
&self,
|
||||||
|
create_session_param: CreateMatchmakeSessionParam,
|
||||||
|
) -> Result<MatchmakeSession, ErrorCode> {
|
||||||
|
println!("{:?}", create_session_param);
|
||||||
|
|
||||||
|
let gid = self.matchmake_manager.next_gid();
|
||||||
|
|
||||||
|
let mut new_session = ExtendedMatchmakeSession::from_matchmake_session(
|
||||||
|
gid,
|
||||||
|
create_session_param.matchmake_session,
|
||||||
|
&self.this.clone(),
|
||||||
|
)
|
||||||
|
.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
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
});
|
||||||
|
for player in players {
|
||||||
|
joining_players.push(Arc::downgrade(&player));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(users);
|
||||||
|
|
||||||
|
new_session.session.participation_count = create_session_param.participation_count as u32;
|
||||||
|
new_session
|
||||||
|
.add_players(&joining_players, create_session_param.join_message)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let session = new_session.session.clone();
|
||||||
|
|
||||||
|
let mut sessions = self.matchmake_manager.sessions.write().await;
|
||||||
|
sessions.insert(gid, Arc::new(Mutex::new(new_session)));
|
||||||
|
drop(sessions);
|
||||||
|
|
||||||
|
Ok(session)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn join_matchmake_session_with_param(
|
||||||
|
&self,
|
||||||
|
join_session_param: JoinMatchmakeSessionParam,
|
||||||
|
) -> Result<MatchmakeSession, ErrorCode> {
|
||||||
|
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));
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
});
|
||||||
|
for player in players {
|
||||||
|
joining_players.push(Arc::downgrade(&player));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(users);
|
||||||
|
|
||||||
|
session
|
||||||
|
.add_players(&joining_players, join_session_param.join_message)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let mm_session = session.session.clone();
|
||||||
|
|
||||||
|
Ok(mm_session)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
for player in players {
|
||||||
|
joining_players.push(Arc::downgrade(&player));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(users);
|
||||||
|
|
||||||
|
let sessions = self.matchmake_manager.sessions.read().await;
|
||||||
|
for session in sessions.values() {
|
||||||
|
let mut session = session.lock().await;
|
||||||
|
|
||||||
|
println!("checking session!");
|
||||||
|
|
||||||
|
if !session.is_joinable() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut bool_matched_criteria = false;
|
||||||
|
|
||||||
|
for criteria in ¶m.search_criteria {
|
||||||
|
if session.matches_criteria(criteria)? {
|
||||||
|
bool_matched_criteria = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if bool_matched_criteria {
|
||||||
|
session
|
||||||
|
.add_players(&joining_players, param.join_message)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
return Ok(session.session.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(sessions);
|
||||||
|
|
||||||
|
println!("making new session!");
|
||||||
|
|
||||||
|
let AutoMatchmakeParam {
|
||||||
|
join_message,
|
||||||
|
participation_count,
|
||||||
|
gid_for_participation_check,
|
||||||
|
matchmake_session,
|
||||||
|
additional_participants,
|
||||||
|
..
|
||||||
|
} = param;
|
||||||
|
|
||||||
|
self.create_matchmake_session_with_param(CreateMatchmakeSessionParam {
|
||||||
|
join_message,
|
||||||
|
participation_count,
|
||||||
|
gid_for_participation_check,
|
||||||
|
create_matchmake_session_option: 0,
|
||||||
|
matchmake_session,
|
||||||
|
additional_participants,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
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> {
|
||||||
|
let session = self.matchmake_manager.get_session(gid).await?;
|
||||||
|
let mut session = session.lock().await;
|
||||||
|
|
||||||
|
session.session.attributes[attrib_index as usize] = attrib_val;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Matchmake for User {
|
||||||
|
async fn unregister_gathering(&self, _gid: u32) -> Result<bool, ErrorCode> {
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
async fn get_session_urls(&self, gid: u32) -> Result<Vec<StationUrl>, ErrorCode> {
|
||||||
|
let session = self.matchmake_manager.get_session(gid).await?;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
println!("{:?}", urls);
|
||||||
|
|
||||||
|
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> {
|
||||||
|
let session = self.matchmake_manager.get_session(gid).await?;
|
||||||
|
let mut session = session.lock().await;
|
||||||
|
|
||||||
|
session.session.gathering.host_pid = self.pid;
|
||||||
|
|
||||||
|
for player in &session.connected_players {
|
||||||
|
let Some(player) = player.upgrade() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
player
|
||||||
|
.remote
|
||||||
|
.process_notification_event(NotificationEvent {
|
||||||
|
notif_type: 110000,
|
||||||
|
pid_source: self.pid,
|
||||||
|
param_1: gid as PID,
|
||||||
|
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 as PID,
|
||||||
|
param_2: self.pid,
|
||||||
|
param_3: 0,
|
||||||
|
str_param: "".to_string(),
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn migrate_gathering_ownership(
|
||||||
|
&self,
|
||||||
|
gid: u32,
|
||||||
|
candidates: Vec<PID>,
|
||||||
|
_participants_only: bool,
|
||||||
|
) -> Result<(), ErrorCode> {
|
||||||
|
let session = self.matchmake_manager.get_session(gid).await?;
|
||||||
|
let mut session = session.lock().await;
|
||||||
|
|
||||||
|
let candidate = candidates.get(0).ok_or(Core_InvalidArgument)?;
|
||||||
|
|
||||||
|
session.session.gathering.owner_pid = *candidate;
|
||||||
|
|
||||||
|
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 as PID,
|
||||||
|
param_2: *candidate as PID,
|
||||||
|
param_3: 0,
|
||||||
|
str_param: "".to_string(),
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MatchmakeExt for User {
|
||||||
|
async fn end_participation(&self, gid: u32, message: String) -> Result<bool, ErrorCode> {
|
||||||
|
let session = self.matchmake_manager.get_session(gid).await?;
|
||||||
|
let mut session = session.lock().await;
|
||||||
|
|
||||||
|
session
|
||||||
|
.remove_player_from_session(self.pid, &message)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NatTraversal for User {
|
||||||
|
async fn report_nat_properties(
|
||||||
|
&self,
|
||||||
|
nat_mapping: u32,
|
||||||
|
nat_filtering: u32,
|
||||||
|
_rtt: u32,
|
||||||
|
) -> Result<(), ErrorCode> {
|
||||||
|
let mut urls = self.station_url.write().await;
|
||||||
|
|
||||||
|
for station_url in urls.iter_mut() {
|
||||||
|
station_url.options.retain(|o| match o {
|
||||||
|
NatMapping(_) | NatFiltering(_) => false,
|
||||||
|
_ => true,
|
||||||
|
});
|
||||||
|
|
||||||
|
station_url.options.push(NatMapping(nat_mapping as u8));
|
||||||
|
station_url.options.push(NatFiltering(nat_filtering as u8));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn report_nat_traversal_result(
|
||||||
|
&self,
|
||||||
|
_cid: u32,
|
||||||
|
_result: bool,
|
||||||
|
_rtt: u32,
|
||||||
|
) -> Result<(), ErrorCode> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn request_probe_initiation(&self, _station_to_probe: String) -> Result<(), ErrorCode> {
|
||||||
|
info!("NO!");
|
||||||
|
Err(RendezVous_AccountExpired)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
);
|
||||||
|
|
||||||
|
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 {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(v) = users.get(&v) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(user) = v.upgrade() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
user.remote
|
||||||
|
.request_probe_initiation(station_to_probe.clone())
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("finished probing");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct CompetitionPostResults {
|
||||||
|
pub splatfest_id: u32,
|
||||||
|
pub score: u32,
|
||||||
|
pub team_id: u8,
|
||||||
|
pub team_win: u8,
|
||||||
|
pub user: PID,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seperate function because I cannot give a fuck right now
|
||||||
|
fn fetch_team_votes(fest_id: u32) -> Result<Vec<u32>, ErrorCode> {
|
||||||
|
let endpoint_votes = env::var("RNEX_SPLATOON_RESULTS_VOTES_GET").map_err(|_| {
|
||||||
|
error!("RNEX_SPLATOON_RESULTS_VOTES_GET not set");
|
||||||
|
ErrorCode::RendezVous_InvalidConfiguration
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let url_votes = format!("{}?splatfest_id={}", endpoint_votes, fest_id);
|
||||||
|
let mut response = ureq::get(&url_votes).call().map_err(|e| {
|
||||||
|
error!("GET for votes failed: {:?}", e);
|
||||||
|
ErrorCode::RendezVous_InvalidConfiguration
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let body = response.body_mut().read_to_string().map_err(|e| {
|
||||||
|
error!("failed to read votes body: {:?}", e);
|
||||||
|
ErrorCode::RendezVous_InvalidConfiguration
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let body = body.trim().trim_start_matches('[').trim_end_matches(']');
|
||||||
|
let votes: Result<Vec<u32>, _> = body.split(',').map(|s| u32::from_str(s.trim())).collect();
|
||||||
|
|
||||||
|
votes.map_err(|e| {
|
||||||
|
error!("failed to parse votes: {:?}", e);
|
||||||
|
ErrorCode::RendezVous_InvalidConfiguration
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ranking for User {
|
||||||
|
async fn competition_ranking_get_param(
|
||||||
|
&self,
|
||||||
|
param: CompetitionRankingGetParam,
|
||||||
|
) -> Result<Vec<CompetitionRankingScoreInfo>, ErrorCode> {
|
||||||
|
let fest_id = param.festival_ids.get(0).copied().unwrap_or(0);
|
||||||
|
|
||||||
|
let endpoint_results = env::var("RNEX_SPLATOON_RESULTS_GET").map_err(|_| {
|
||||||
|
error!("RNEX_SPLATOON_RESULTS_GET not set");
|
||||||
|
ErrorCode::RendezVous_InvalidConfiguration
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let url_results = format!("{}?splatfest_id={}", endpoint_results, fest_id);
|
||||||
|
let response_results = ureq::get(&url_results).call();
|
||||||
|
|
||||||
|
let results: Vec<CompetitionPostResults> = match response_results {
|
||||||
|
Ok(mut res) => res.body_mut().read_json().map_err(|e| {
|
||||||
|
error!("failed to parse JSON: {:?}", e);
|
||||||
|
ErrorCode::RendezVous_InvalidConfiguration
|
||||||
|
})?,
|
||||||
|
Err(e) => {
|
||||||
|
error!("GET failed: {:?}", e);
|
||||||
|
return Err(ErrorCode::RendezVous_InvalidConfiguration);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let team_votes = fetch_team_votes(fest_id)?;
|
||||||
|
let mut wins = vec![0u32, 0u32];
|
||||||
|
for r in &results {
|
||||||
|
let won_team = r.team_id ^ (!r.team_win);
|
||||||
|
if let Some(team) = wins.get_mut(won_team as usize) {
|
||||||
|
*team += 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let score_data: Vec<CompetitionRankingScoreData> = results
|
||||||
|
.iter()
|
||||||
|
.map(|r| CompetitionRankingScoreData {
|
||||||
|
unk: 1,
|
||||||
|
pid: r.user,
|
||||||
|
score: r.score,
|
||||||
|
modified: KerberosDateTime::now(),
|
||||||
|
unk2: 1,
|
||||||
|
appdata: QBuffer(vec![]),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let info = CompetitionRankingScoreInfo {
|
||||||
|
fest_id,
|
||||||
|
score_data,
|
||||||
|
unk: 0,
|
||||||
|
team_wins: wins,
|
||||||
|
team_votes,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(vec![info])
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn upload_competition_ranking_score(
|
||||||
|
&self,
|
||||||
|
param: UploadCompetitionData,
|
||||||
|
) -> Result<bool, ErrorCode> {
|
||||||
|
info!("fest results for user {:?}:", self.pid);
|
||||||
|
info!("fest id: {:?}", param.splatfest_id);
|
||||||
|
info!("score: {:?}", param.score);
|
||||||
|
info!("team id: {:?}", param.team_id);
|
||||||
|
info!("did current team win: {:?}", param.team_win);
|
||||||
|
|
||||||
|
let endpoint = match env::var("RNEX_SPLATOON_RESULTS_POST") {
|
||||||
|
Ok(url) => url,
|
||||||
|
Err(_) => {
|
||||||
|
error!("RNEX_SPLATOON_RESULTS_POST not set");
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let payload = CompetitionPostResults {
|
||||||
|
splatfest_id: param.splatfest_id,
|
||||||
|
score: param.score,
|
||||||
|
team_id: param.team_id,
|
||||||
|
team_win: param.team_win,
|
||||||
|
user: self.pid,
|
||||||
|
};
|
||||||
|
|
||||||
|
let json_body = match serde_json::to_string(&payload) {
|
||||||
|
Ok(j) => j,
|
||||||
|
Err(e) => {
|
||||||
|
error!("error making json_body: {:?}", e);
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = ureq::post(&endpoint)
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.send(json_body);
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Ok(res) => {
|
||||||
|
info!("POST worked: {}", res.status());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("POST borked: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
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;
|
||||||
41
rnex-core/src/prudp/socket_addr.rs
Normal file
41
rnex-core/src/prudp/socket_addr.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
use hmac::Hmac;
|
||||||
|
use macros::RmcSerialize;
|
||||||
|
use md5::digest::Mac;
|
||||||
|
use rnex_core::prudp::virtual_port::VirtualPort;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::net::{IpAddr, SocketAddr};
|
||||||
|
|
||||||
|
type Md5Hmac = Hmac<md5::Md5>;
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Hash, Debug, Copy, Clone, Ord, PartialOrd, RmcSerialize)]
|
||||||
|
#[rmc_struct(0)]
|
||||||
|
pub struct PRUDPSockAddr {
|
||||||
|
pub regular_socket_addr: SocketAddr,
|
||||||
|
pub virtual_port: VirtualPort,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PRUDPSockAddr {
|
||||||
|
pub fn new(regular_socket_addr: SocketAddr, virtual_port: VirtualPort) -> Self {
|
||||||
|
Self {
|
||||||
|
regular_socket_addr,
|
||||||
|
virtual_port,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_connection_signature(&self) -> [u8; 16] {
|
||||||
|
let mut hmac = Md5Hmac::new_from_slice(&[0; 16]).expect("?");
|
||||||
|
|
||||||
|
let data = match self.regular_socket_addr.ip() {
|
||||||
|
IpAddr::V4(v) => v.octets().to_vec(),
|
||||||
|
IpAddr::V6(v) => v.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");
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
173
rnex-core/src/prudp/station_url.rs
Normal file
173
rnex-core/src/prudp/station_url.rs
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
use crate::prudp::station_url::Type::{PRUDP, PRUDPS, UDP};
|
||||||
|
use crate::prudp::station_url::UrlOptions::{
|
||||||
|
Address, ConnectionID, NatFiltering, NatMapping, NatType, PID, PMP, Platform, Port,
|
||||||
|
PrincipalID, RVConnectionID, StreamID, StreamType, UPNP,
|
||||||
|
};
|
||||||
|
use crate::rmc::structures::Error::StationUrlInvalid;
|
||||||
|
use crate::rmc::structures::RmcSerialize;
|
||||||
|
use crate::rmc::structures::helpers::DummyFormatWriter;
|
||||||
|
use log::error;
|
||||||
|
use std::fmt::{Debug, Display, Formatter, Write};
|
||||||
|
use std::io::Read;
|
||||||
|
use std::net::IpAddr;
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum Type {
|
||||||
|
UDP,
|
||||||
|
PRUDP,
|
||||||
|
PRUDPS,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod nat_types {
|
||||||
|
pub const BEHIND_NAT: u8 = 1;
|
||||||
|
pub const PUBLIC: u8 = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
|
pub enum UrlOptions {
|
||||||
|
Address(IpAddr),
|
||||||
|
Port(u16),
|
||||||
|
StreamType(u8),
|
||||||
|
StreamID(u8),
|
||||||
|
ConnectionID(u8),
|
||||||
|
PrincipalID(rnex_core::PID),
|
||||||
|
NatType(u8),
|
||||||
|
NatMapping(u8),
|
||||||
|
NatFiltering(u8),
|
||||||
|
UPNP(u8),
|
||||||
|
RVConnectionID(u32),
|
||||||
|
Platform(u8),
|
||||||
|
PMP(u8),
|
||||||
|
PID(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
|
pub struct StationUrl {
|
||||||
|
pub url_type: Type,
|
||||||
|
pub options: Vec<UrlOptions>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StationUrl {
|
||||||
|
pub fn read_options(options: &str) -> Option<Vec<UrlOptions>> {
|
||||||
|
let mut options_out = Vec::new();
|
||||||
|
|
||||||
|
for option in options.split(';') {
|
||||||
|
if option == "" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut option_parts = option.split('=');
|
||||||
|
let option_name = option_parts.next()?.to_ascii_lowercase();
|
||||||
|
let option_value = option_parts.next()?;
|
||||||
|
|
||||||
|
match option_name.as_ref() {
|
||||||
|
"address" => options_out.push(Address(option_value.parse().ok()?)),
|
||||||
|
"port" => options_out.push(Port(option_value.parse().ok()?)),
|
||||||
|
"natf" => options_out.push(NatFiltering(option_value.parse().ok()?)),
|
||||||
|
"natm" => options_out.push(NatMapping(option_value.parse().ok()?)),
|
||||||
|
"sid" => options_out.push(StreamID(option_value.parse().ok()?)),
|
||||||
|
"upnp" => options_out.push(UPNP(option_value.parse().ok()?)),
|
||||||
|
"type" => options_out.push(NatType(option_value.parse().ok()?)),
|
||||||
|
"stream" => options_out.push(StreamType(option_value.parse().ok()?)),
|
||||||
|
"RVCID" => options_out.push(RVConnectionID(option_value.parse().ok()?)),
|
||||||
|
"rvcid" => options_out.push(RVConnectionID(option_value.parse().ok()?)),
|
||||||
|
"pl" => options_out.push(Platform(option_value.parse().ok()?)),
|
||||||
|
"pmp" => options_out.push(PMP(option_value.parse().ok()?)),
|
||||||
|
"pid" => options_out.push(PID(option_value.parse().ok()?)),
|
||||||
|
"PID" => options_out.push(PID(option_value.parse().ok()?)),
|
||||||
|
_ => {
|
||||||
|
error!("unimplemented option type, skipping: {}", option_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(options_out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(())?);
|
||||||
|
|
||||||
|
let options = &options[2..];
|
||||||
|
|
||||||
|
let url_type = match url_type {
|
||||||
|
"udp" => UDP,
|
||||||
|
"prudp" => PRUDP,
|
||||||
|
"prudps" => PRUDPS,
|
||||||
|
_ => return Err(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let options = Self::read_options(options).ok_or(())?;
|
||||||
|
|
||||||
|
Ok(Self { url_type, options })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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:/",
|
||||||
|
};
|
||||||
|
write!(f, "{}", url_type_str)?;
|
||||||
|
|
||||||
|
for option in &self.options {
|
||||||
|
match option {
|
||||||
|
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!(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 RmcSerialize for StationUrl {
|
||||||
|
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 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 {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let str: String = self.into();
|
||||||
|
f.write_str(&str)
|
||||||
|
}
|
||||||
|
}
|
||||||
79
rnex-core/src/prudp/ticket.rs
Normal file
79
rnex-core/src/prudp/ticket.rs
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
use log::{error, info};
|
||||||
|
use rc4::{KeyInit, Rc4, Rc4Core, StreamCipher, cipher::StreamCipherCoreWrapper};
|
||||||
|
use typenum::U16;
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
use rnex_core::PID;
|
||||||
|
|
||||||
|
pub fn read_secure_connection_data(
|
||||||
|
data: &[u8],
|
||||||
|
act: &Account,
|
||||||
|
) -> Option<([u8; SESSION_KEY_LENGTH], PID, 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 {}", SESSION_KEY_LENGTH);
|
||||||
|
|
||||||
|
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: PID = 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 std::io::{Read, Seek, Write};
|
||||||
use bytemuck::bytes_of;
|
use bytemuck::bytes_of;
|
||||||
use log::error;
|
use log::error;
|
||||||
use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions};
|
use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions};
|
||||||
use crate::rmc::response::{ErrorCode, RMCResponseResult};
|
use crate::rmc::response::{ErrorCode, RMCResponseResult};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
|
@ -42,7 +42,8 @@ impl RMCMessage{
|
||||||
error!("received incorrect rmc packet: expected size {} but found {}", size, header_size + rest_of_data.len());
|
error!("received incorrect rmc packet: expected size {} but found {}", size, header_size + rest_of_data.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// println!("rmc packet: protoid: {}, method id: {}", protocol_id, method_id);
|
||||||
|
// println!("{}", hex::encode(&rest_of_data));
|
||||||
|
|
||||||
//stream.
|
//stream.
|
||||||
Ok(Self{
|
Ok(Self{
|
||||||
30
rnex-core/src/rmc/protocols/account_management.rs
Normal file
30
rnex-core/src/rmc/protocols/account_management.rs
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
use macros::{RmcSerialize, method_id, rmc_proto};
|
||||||
|
|
||||||
|
use rnex_core::{
|
||||||
|
PID,
|
||||||
|
rmc::{response::ErrorCode, structures::any::Any},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{kerberos::KerberosDateTime, rmc::protocols::friends::NNAInfo};
|
||||||
|
|
||||||
|
#[derive(RmcSerialize, Debug, Clone)]
|
||||||
|
#[rmc_struct(0)]
|
||||||
|
pub struct NintendoCreateAccountData {
|
||||||
|
pub nna_info: NNAInfo,
|
||||||
|
pub nex_token: String,
|
||||||
|
pub birthday: KerberosDateTime,
|
||||||
|
pub unk: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rmc_proto(25)]
|
||||||
|
pub trait AccountManagement {
|
||||||
|
#[method_id(27)]
|
||||||
|
async fn nintendo_create_account(
|
||||||
|
&self,
|
||||||
|
principal_name: String,
|
||||||
|
key: String,
|
||||||
|
groups: u32,
|
||||||
|
email: String,
|
||||||
|
auth_data: Any,
|
||||||
|
) -> Result<(PID, String), ErrorCode>;
|
||||||
|
}
|
||||||
|
|
@ -1,36 +1,47 @@
|
||||||
use crate::rmc::response::ErrorCode;
|
use crate::rmc::structures::connection_data::{ConnectionData, ConnectionDataOld};
|
||||||
use crate::rmc::structures::any::Any;
|
use cfg_if::cfg_if;
|
||||||
use crate::rmc::structures::connection_data::ConnectionData;
|
|
||||||
use crate::rmc::structures::qresult::QResult;
|
|
||||||
use macros::{method_id, rmc_proto};
|
use macros::{method_id, rmc_proto};
|
||||||
|
use rnex_core::PID;
|
||||||
|
use rnex_core::rmc::response::ErrorCode;
|
||||||
|
use rnex_core::rmc::structures::any::Any;
|
||||||
|
use rnex_core::rmc::structures::qresult::QResult;
|
||||||
|
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(feature = "nx")]{
|
||||||
|
type LOGIN_EX_RET = (QResult, PID, Vec<u8>, ConnectionData, String, String);
|
||||||
|
type REQUEST_TICKET_RET = (QResult, Vec<u8>, String);
|
||||||
|
} else {
|
||||||
|
type LOGIN_EX_RET = (QResult, PID, Vec<u8>, ConnectionData, String);
|
||||||
|
type REQUEST_TICKET_RET = (QResult, Vec<u8>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This is the representation for `Ticket Granting`(for details see the
|
/// This is the representation for `Ticket Granting`(for details see the
|
||||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||||
#[rmc_proto(10)]
|
#[rmc_proto(10)]
|
||||||
pub trait Auth {
|
pub trait Auth {
|
||||||
/// representation of the `Login` method(for details see the
|
/// representation of the `Login` method(for details see the
|
||||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||||
#[method_id(1)]
|
#[method_id(1)]
|
||||||
async fn login(&self, name: String) -> Result<(), ErrorCode>;
|
async fn login(
|
||||||
|
&self,
|
||||||
|
name: String,
|
||||||
|
) -> Result<(QResult, PID, Vec<u8>, ConnectionDataOld, String), ErrorCode>;
|
||||||
|
|
||||||
/// representation of the `LoginEx` method(for details see the
|
/// representation of the `LoginEx` method(for details see the
|
||||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||||
#[method_id(2)]
|
#[method_id(2)]
|
||||||
async fn login_ex(
|
async fn login_ex(&self, name: String, extra_data: Any) -> Result<LOGIN_EX_RET, ErrorCode>;
|
||||||
&self,
|
|
||||||
name: String,
|
|
||||||
extra_data: Any,
|
|
||||||
) -> Result<(QResult, u32, Vec<u8>, ConnectionData, String), ErrorCode>;
|
|
||||||
|
|
||||||
/// representation of the `RequestTicket` method(for details see the
|
|
||||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
|
||||||
#[method_id(3)]
|
#[method_id(3)]
|
||||||
async fn request_ticket(
|
async fn request_ticket(
|
||||||
&self,
|
&self,
|
||||||
source_pid: u32,
|
source_pid: PID,
|
||||||
destination_pid: u32,
|
destination_pid: PID,
|
||||||
) -> Result<(QResult, Vec<u8>), ErrorCode>;
|
) -> Result<REQUEST_TICKET_RET, ErrorCode>;
|
||||||
|
|
||||||
|
/// representation of the `RequestTicket` method(for details see the
|
||||||
|
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||||
|
|
||||||
/// representation of the `GetPID` method(for details see the
|
/// representation of the `GetPID` method(for details see the
|
||||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||||
|
|
@ -40,7 +51,7 @@ pub trait Auth {
|
||||||
/// representation of the `LoginWithContext` method(for details see the
|
/// representation of the `LoginWithContext` method(for details see the
|
||||||
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
/// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol))
|
||||||
#[method_id(5)]
|
#[method_id(5)]
|
||||||
async fn get_name(&self, pid: u32) -> Result<String, ErrorCode>;
|
async fn get_name(&self, pid: PID) -> Result<String, ErrorCode>;
|
||||||
|
|
||||||
// `LoginWithContext` is left out here because we don't need it right now and versioning still
|
// `LoginWithContext` is left out here because we don't need it right now and versioning still
|
||||||
// needs to be figured out
|
// needs to be figured out
|
||||||
181
rnex-core/src/rmc/protocols/friends.rs
Normal file
181
rnex-core/src/rmc/protocols/friends.rs
Normal file
|
|
@ -0,0 +1,181 @@
|
||||||
|
use macros::{RmcSerialize, method_id, rmc_proto};
|
||||||
|
|
||||||
|
use rnex_core::{kerberos::KerberosDateTime, rmc::response::ErrorCode};
|
||||||
|
|
||||||
|
use rnex_core::rmc::structures::{data::Data, rmc_struct};
|
||||||
|
|
||||||
|
#[derive(RmcSerialize, Debug, Clone)]
|
||||||
|
#[rmc_struct(0)]
|
||||||
|
pub struct MiiV2 {
|
||||||
|
#[extends]
|
||||||
|
pub data: Data,
|
||||||
|
pub name: String,
|
||||||
|
pub unk: u8,
|
||||||
|
pub unk2: u8,
|
||||||
|
pub mii_data: Vec<u8>,
|
||||||
|
pub date_time: KerberosDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(RmcSerialize, Debug, Clone)]
|
||||||
|
#[rmc_struct(0)]
|
||||||
|
pub struct PrincipalBasicInfo {
|
||||||
|
#[extends]
|
||||||
|
pub data: Data,
|
||||||
|
pub pid: u32,
|
||||||
|
pub nnid: String,
|
||||||
|
pub mii: MiiV2,
|
||||||
|
pub unk: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(RmcSerialize, Debug, Clone)]
|
||||||
|
#[rmc_struct(0)]
|
||||||
|
pub struct NNAInfo {
|
||||||
|
#[extends]
|
||||||
|
pub data: Data,
|
||||||
|
pub principal_basic_info: PrincipalBasicInfo,
|
||||||
|
pub unk: u8,
|
||||||
|
pub unk2: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(RmcSerialize, Clone, Copy, Debug)]
|
||||||
|
#[rmc_struct(0)]
|
||||||
|
pub struct GameKey {
|
||||||
|
#[extends]
|
||||||
|
pub data: Data,
|
||||||
|
pub tid: u64,
|
||||||
|
pub version: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(RmcSerialize, Clone, Debug)]
|
||||||
|
#[rmc_struct(0)]
|
||||||
|
pub struct NintendoPresenceV2 {
|
||||||
|
#[extends]
|
||||||
|
pub data: Data,
|
||||||
|
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 {
|
||||||
|
#[extends]
|
||||||
|
pub data: Data,
|
||||||
|
pub show_online: bool,
|
||||||
|
pub show_playing_title: bool,
|
||||||
|
pub block_friend_request: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(RmcSerialize)]
|
||||||
|
#[rmc_struct(0)]
|
||||||
|
pub struct Comment {
|
||||||
|
#[extends]
|
||||||
|
pub data: Data,
|
||||||
|
pub unk: u8,
|
||||||
|
pub message: String,
|
||||||
|
pub last_changed: KerberosDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(RmcSerialize)]
|
||||||
|
#[rmc_struct(0)]
|
||||||
|
pub struct FriendInfo {
|
||||||
|
#[extends]
|
||||||
|
pub data: Data,
|
||||||
|
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 {
|
||||||
|
#[extends]
|
||||||
|
pub data: Data,
|
||||||
|
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 {
|
||||||
|
#[extends]
|
||||||
|
pub data: Data,
|
||||||
|
pub basic_info: PrincipalBasicInfo,
|
||||||
|
pub request_message: FriendRequestMessage,
|
||||||
|
pub sent_on: KerberosDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(RmcSerialize)]
|
||||||
|
#[rmc_struct(0)]
|
||||||
|
pub struct BlacklistedPrincipal {
|
||||||
|
#[extends]
|
||||||
|
pub data: Data,
|
||||||
|
pub basic_info: PrincipalBasicInfo,
|
||||||
|
pub game_key: GameKey,
|
||||||
|
pub since: KerberosDateTime,
|
||||||
|
}
|
||||||
|
#[derive(RmcSerialize)]
|
||||||
|
#[rmc_struct(0)]
|
||||||
|
pub struct PersistentNotification {
|
||||||
|
#[extends]
|
||||||
|
pub data: Data,
|
||||||
|
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,
|
||||||
|
>;
|
||||||
|
#[method_id(13)]
|
||||||
|
async fn update_presence(&self, presence: NintendoPresenceV2) -> Result<(), ErrorCode>;
|
||||||
|
#[method_id(18)]
|
||||||
|
async fn delete_persistent_notification(
|
||||||
|
&self,
|
||||||
|
notifs: Vec<PersistentNotification>,
|
||||||
|
) -> Result<(), ErrorCode>;
|
||||||
|
#[method_id(19)]
|
||||||
|
async fn check_setting_status(&self) -> Result<u8, ErrorCode>;
|
||||||
|
}
|
||||||
|
|
@ -1,17 +1,22 @@
|
||||||
use macros::{method_id, rmc_proto};
|
use macros::{method_id, rmc_proto};
|
||||||
use crate::prudp::station_url::StationUrl;
|
use rnex_core::prudp::station_url::StationUrl;
|
||||||
use crate::rmc::response::ErrorCode;
|
use rnex_core::rmc::response::ErrorCode;
|
||||||
|
|
||||||
|
use rnex_core::PID;
|
||||||
|
|
||||||
#[rmc_proto(21)]
|
#[rmc_proto(21)]
|
||||||
pub trait Matchmake{
|
pub trait Matchmake {
|
||||||
#[method_id(2)]
|
#[method_id(2)]
|
||||||
async fn unregister_gathering(&self, gid: u32) -> Result<bool, ErrorCode>;
|
async fn unregister_gathering(&self, gid: u32) -> Result<bool, ErrorCode>;
|
||||||
#[method_id(41)]
|
#[method_id(41)]
|
||||||
async fn get_session_urls(&self, gid: u32) -> Result<Vec<StationUrl>, ErrorCode>;
|
async fn get_session_urls(&self, gid: u32) -> Result<Vec<StationUrl>, ErrorCode>;
|
||||||
|
|
||||||
#[method_id(42)]
|
#[method_id(42)]
|
||||||
async fn update_session_host(&self, gid: u32, change_owner: bool) -> Result<(), ErrorCode>;
|
async fn update_session_host(&self, gid: u32, change_owner: bool) -> Result<(), ErrorCode>;
|
||||||
|
|
||||||
#[method_id(44)]
|
#[method_id(44)]
|
||||||
async fn migrate_gathering_ownership(&self, gid: u32, candidates: Vec<u32>, participants_only: bool) -> Result<(), ErrorCode>;
|
async fn migrate_gathering_ownership(
|
||||||
}
|
&self,
|
||||||
|
gid: u32,
|
||||||
|
candidates: Vec<PID>,
|
||||||
|
participants_only: bool,
|
||||||
|
) -> Result<(), ErrorCode>;
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use macros::{method_id, rmc_proto};
|
use macros::{method_id, rmc_proto};
|
||||||
use crate::rmc::response::ErrorCode;
|
use rnex_core::rmc::response::ErrorCode;
|
||||||
|
|
||||||
#[rmc_proto(50)]
|
#[rmc_proto(50)]
|
||||||
pub trait MatchmakeExt{
|
pub trait MatchmakeExt{
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use macros::{method_id, rmc_proto};
|
use macros::{method_id, rmc_proto};
|
||||||
use crate::rmc::response::ErrorCode;
|
use rnex_core::rmc::response::ErrorCode;
|
||||||
use crate::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession};
|
use rnex_core::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession};
|
||||||
|
|
||||||
#[rmc_proto(109)]
|
#[rmc_proto(109)]
|
||||||
pub trait MatchmakeExtension{
|
pub trait MatchmakeExtension{
|
||||||
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