diff --git a/.ci-scripts/make-edition.sh b/.ci-scripts/make-edition.sh new file mode 100755 index 0000000..57c857a --- /dev/null +++ b/.ci-scripts/make-edition.sh @@ -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" diff --git a/.devcontainer.json b/.devcontainer.json new file mode 100644 index 0000000..ac711b7 --- /dev/null +++ b/.devcontainer.json @@ -0,0 +1,3 @@ +{ + "image": "ci.virintox.com/spfn/rust-nex/dev-container:latest" +} diff --git a/.dockerignore b/.dockerignore index 0d95126..4e09ffe 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,10 @@ .env -target \ No newline at end of file +target +.dockerignore +Dockerfile +CODE_OF_CONDUCT.md +CONTRIBUTING.md +README.md +.gitignore +LICENSE +.devcontainer.json \ No newline at end of file diff --git a/.gitignore b/.gitignore index ea0ae4a..2a7a556 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ target .idea .env log -reports \ No newline at end of file +reports +.zed diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 56c6940..900885c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,19 +1,26 @@ -image: docker:latest +default: + image: quay.io/podman/stable + cache: + key: image-cache + paths: + - /var/lib/containers/storage + - /run/containers/storage + - .local/share/containers/storage + before_script: + - git submodule update --init + - podman login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY variables: - IMAGE_NAME: "ci.perditum.com/perditum/rnex-splatoon" + DOCKER_TLS_CERTDIR: "/certs" IMAGE_TAG: "${CI_COMMIT_REF_SLUG}" -before_script: - - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" ci.perditum.com - stages: - - build-and-push + - build_and_test -build-and-push: - stage: build-and-push - script: - - docker build -t "$IMAGE_NAME:$IMAGE_TAG" . - - docker tag "$IMAGE_NAME:$IMAGE_TAG" "$IMAGE_NAME:latest" - - docker push "$IMAGE_NAME:$IMAGE_TAG" - - docker push "$IMAGE_NAME:latest" +splatoon: + stage: build_and_test + script: ./.ci-scripts/make-edition.sh splatoon + +friends: + stage: build_and_test + script: ./.ci-scripts/make-edition.sh friends diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 64cd88d..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "grpc-protobufs"] - path = grpc-protobufs - url = https://github.com/PretendoNetwork/grpc-protobufs.git diff --git a/Cargo.lock b/Cargo.lock index fcc53fc..ce7b5e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,15 +17,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - [[package]] name = "android-tzdata" version = "0.1.1" @@ -43,31 +34,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" - -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.102", -] +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "async-trait" @@ -77,105 +46,14 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", ] -[[package]] -name = "atomic" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" - -[[package]] -name = "atomic" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - -[[package]] -name = "aws-lc-rs" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fcc8f365936c834db5514fc45aee5b1202d677e6b40e48468aaaa8183ca8c7" -dependencies = [ - "aws-lc-sys", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b1d86e7705efe1be1b569bab41d4fa1e14e220b60a160f78de2db687add079" -dependencies = [ - "bindgen", - "cc", - "cmake", - "dunce", - "fs_extra", -] - -[[package]] -name = "axum" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" -dependencies = [ - "async-trait", - "axum-core", - "bytes", - "futures-util", - "http 1.3.1", - "http-body 1.0.1", - "http-body-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "sync_wrapper", - "tower 0.5.2", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum-core" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http 1.3.1", - "http-body 1.0.1", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper", - "tower-layer", - "tower-service", -] +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" @@ -189,7 +67,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -198,41 +76,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base64ct" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" - -[[package]] -name = "binascii" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" - -[[package]] -name = "bindgen" -version = "0.69.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "itertools 0.12.1", - "lazy_static", - "lazycell", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.102", - "which", -] - [[package]] name = "bitflags" version = "2.9.1" @@ -250,9 +93,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.18.1" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytemuck" @@ -265,21 +108,15 @@ dependencies = [ [[package]] name = "bytemuck_derive" -version = "1.9.3" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" +checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", ] -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "bytes" version = "1.10.1" @@ -288,46 +125,18 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.26" +version = "1.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac" +checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" dependencies = [ - "jobserver", - "libc", "shlex", ] -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chacha20" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" @@ -340,7 +149,7 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -353,32 +162,6 @@ dependencies = [ "inout", ] -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "cmake" -version = "0.1.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" -dependencies = [ - "cc", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - [[package]] name = "cookie" version = "0.18.1" @@ -391,10 +174,28 @@ dependencies = [ ] [[package]] -name = "core-foundation" -version = "0.9.4" +name = "cookie_store" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +checksum = "3fc4bff745c9b4c7fb1e97b25d13153da2bc7796260141df62378998d070207f" +dependencies = [ + "cookie", + "document-features", + "idna", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", @@ -415,6 +216,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -425,32 +235,11 @@ dependencies = [ "typenum", ] -[[package]] -name = "ctrlc" -version = "3.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73" -dependencies = [ - "nix", - "windows-sys 0.59.0", -] - [[package]] name = "data-encoding" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" - -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", -] +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "deranged" @@ -461,39 +250,6 @@ dependencies = [ "powerfmt", ] -[[package]] -name = "devise" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1d90b0c4c777a2cad215e3c7be59ac7c15adf45cf76317009b7d096d46f651d" -dependencies = [ - "devise_codegen", - "devise_core", -] - -[[package]] -name = "devise_codegen" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71b28680d8be17a570a2334922518be6adc3f58ecc880cbb404eaeb8624fd867" -dependencies = [ - "devise_core", - "quote", -] - -[[package]] -name = "devise_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b035a542cf7abf01f2e3c4d5a7acbaebfefe120ae4efc7bde3df98186e4b8af7" -dependencies = [ - "bitflags", - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn 2.0.102", -] - [[package]] name = "digest" version = "0.10.7" @@ -501,7 +257,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", - "const-oid", "crypto-common", "subtle", ] @@ -514,7 +269,16 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", ] [[package]] @@ -523,27 +287,6 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - [[package]] name = "equivalent" version = "1.0.2" @@ -551,125 +294,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "errno" -version = "0.3.12" +name = "flate2" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ - "libc", - "windows-sys 0.59.0", + "crc32fast", + "miniz_oxide", ] -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "figment" -version = "0.10.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" -dependencies = [ - "atomic 0.6.0", - "pear", - "serde", - "toml", - "uncased", - "version_check", -] - -[[package]] -name = "fixedbitset" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" - [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - [[package]] name = "futures-macro" version = "0.3.31" @@ -678,7 +332,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", ] [[package]] @@ -699,31 +353,15 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ - "futures-channel", "futures-core", - "futures-io", "futures-macro", "futures-sink", "futures-task", - "memchr", "pin-project-lite", "pin-utils", "slab", ] -[[package]] -name = "generator" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" -dependencies = [ - "cc", - "libc", - "log", - "rustversion", - "windows", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -742,19 +380,19 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasip2", ] [[package]] @@ -763,73 +401,11 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" -[[package]] -name = "glob" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" - -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap 2.9.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "h2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http 1.3.1", - "indexmap 2.9.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "hashbrown" -version = "0.12.3" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.15.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "hex" @@ -846,26 +422,6 @@ dependencies = [ "digest", ] -[[package]] -name = "home" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.3.1" @@ -877,168 +433,12 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http 1.3.1", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http 1.3.1", - "http-body 1.0.1", - "pin-project-lite", -] - [[package]] name = "httparse" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "0.14.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "h2 0.4.10", - "http 1.3.1", - "http-body 1.0.1", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" -dependencies = [ - "http 1.3.1", - "hyper 1.6.0", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", -] - -[[package]] -name = "hyper-timeout" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" -dependencies = [ - "hyper 1.6.0", - "hyper-util", - "pin-project-lite", - "tokio", - "tower-service", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper 1.6.0", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" -dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http 1.3.1", - "http-body 1.0.1", - "hyper 1.6.0", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "system-configuration", - "tokio", - "tower-service", - "tracing", - "windows-registry", -] - [[package]] name = "iana-time-zone" version = "0.1.63" @@ -1065,9 +465,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -1078,9 +478,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -1091,11 +491,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -1106,42 +505,38 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -1151,9 +546,9 @@ dependencies = [ [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -1172,31 +567,14 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.3" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "indexmap" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff" dependencies = [ "equivalent", - "hashbrown 0.15.4", - "serde", + "hashbrown", ] -[[package]] -name = "inlinable_string" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" - [[package]] name = "inout" version = "0.1.4" @@ -1207,48 +585,14 @@ dependencies = [ ] [[package]] -name = "ipnet" -version = "2.11.0" +name = "io-uring" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" - -[[package]] -name = "iri-string" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "is-terminal" -version = "0.4.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" -dependencies = [ - "hermit-abi", + "bitflags", + "cfg-if", "libc", - "windows-sys 0.59.0", -] - -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", ] [[package]] @@ -1257,16 +601,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" -[[package]] -name = "jobserver" -version = "0.1.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" -dependencies = [ - "getrandom 0.3.3", - "libc", -] - [[package]] name = "js-sys" version = "0.3.77" @@ -1283,60 +617,23 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" -version = "0.2.172" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" - -[[package]] -name = "libloading" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" -dependencies = [ - "cfg-if", - "windows-targets 0.53.1", -] - -[[package]] -name = "libm" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" - -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - -[[package]] -name = "linux-raw-sys" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "lock_api" @@ -1354,46 +651,25 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" -[[package]] -name = "loom" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" -dependencies = [ - "cfg-if", - "generator", - "scoped-tls", - "serde", - "serde_json", - "tracing", - "tracing-subscriber", -] - [[package]] name = "macros" version = "0.0.0" dependencies = [ "proc-macro2", "quote", - "rand 0.9.1", - "syn 2.0.102", + "syn 2.0.104", ] [[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +name = "macros" +version = "0.1.1" +source = "git+https://github.com/RusticMaple/VByteMacros#e2f31bded8c5591e847ba03faf79ae0351e43e69" dependencies = [ - "regex-automata 0.1.10", + "proc-macro2", + "quote", + "syn 1.0.109", ] -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - [[package]] name = "md-5" version = "0.10.6" @@ -1410,18 +686,6 @@ version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniz_oxide" version = "0.8.9" @@ -1429,6 +693,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -1438,127 +703,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", "windows-sys 0.59.0", ] -[[package]] -name = "multer" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" -dependencies = [ - "bytes", - "encoding_rs", - "futures-util", - "http 1.3.1", - "httparse", - "memchr", - "mime", - "spin", - "tokio", - "tokio-util", - "version_check", -] - -[[package]] -name = "multimap" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" - -[[package]] -name = "native-tls" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nix" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" -dependencies = [ - "bitflags", - "cfg-if", - "cfg_aliases", - "libc", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "smallvec", - "zeroize", -] - [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -1566,17 +720,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi", - "libc", ] [[package]] @@ -1603,55 +746,11 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -[[package]] -name = "openssl" -version = "0.10.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.102", -] - [[package]] name = "openssl-probe" -version = "0.1.6" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - -[[package]] -name = "openssl-sys" -version = "0.9.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391" [[package]] name = "parking_lot" @@ -1673,7 +772,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -1682,74 +781,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "pear" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" -dependencies = [ - "inlinable_string", - "pear_codegen", - "yansi", -] - -[[package]] -name = "pear_codegen" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" -dependencies = [ - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn 2.0.102", -] - -[[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "petgraph" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" -dependencies = [ - "fixedbitset", - "indexmap 2.9.0", -] - -[[package]] -name = "pin-project" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.102", -] - [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1762,38 +799,11 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der", - "pkcs8", - "spki", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -1813,16 +823,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "prettyplease" -version = "0.2.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dee91521343f4c5c6a63edd65e54f31f5c92fe8978c40a4282f8372194c6a7d" -dependencies = [ - "proc-macro2", - "syn 2.0.102", -] - [[package]] name = "proc-macro2" version = "1.0.95" @@ -1833,68 +833,78 @@ dependencies = [ ] [[package]] -name = "proc-macro2-diagnostics" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +name = "proxy" +version = "0.1.0" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.102", - "version_check", - "yansi", -] - -[[package]] -name = "prost" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-build" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" -dependencies = [ - "heck", - "itertools 0.14.0", + "cfg-if", "log", - "multimap", + "proxy-common", + "prudplite", + "prudpv0", + "prudpv1", + "rnex-core", + "tokio", +] + +[[package]] +name = "proxy-common" +version = "0.1.0" +dependencies = [ + "hex", + "log", + "rnex-core", + "thiserror", + "tokio", +] + +[[package]] +name = "prudplite" +version = "0.1.0" +dependencies = [ + "bytemuck", + "futures-util", + "log", + "proxy-common", + "rnex-core", + "tokio", + "tokio-tungstenite", + "v-byte-helpers", +] + +[[package]] +name = "prudpv0" +version = "0.1.0" +dependencies = [ + "bytemuck", + "cfg-if", + "hmac", + "log", + "md-5", + "proxy-common", + "rc4", + "rnex-core", + "tokio", + "typenum", +] + +[[package]] +name = "prudpv1" +version = "0.1.0" +dependencies = [ + "async-trait", + "bytemuck", + "cfg-if", + "hmac", + "log", + "md-5", "once_cell", - "petgraph", - "prettyplease", - "prost", - "prost-types", - "regex", - "syn 2.0.102", - "tempfile", -] - -[[package]] -name = "prost-derive" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" -dependencies = [ - "anyhow", - "itertools 0.14.0", - "proc-macro2", - "quote", - "syn 2.0.102", -] - -[[package]] -name = "prost-types" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" -dependencies = [ - "prost", + "proxy-common", + "rc4", + "rnex-core", + "thiserror", + "tokio", + "typenum", + "v-byte-helpers", ] [[package]] @@ -1908,9 +918,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" @@ -1925,12 +935,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -1950,7 +960,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -1964,11 +974,11 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", ] [[package]] @@ -1982,117 +992,13 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ "bitflags", ] -[[package]] -name = "ref-cast" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.102", -] - -[[package]] -name = "regex" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - -[[package]] -name = "reqwest" -version = "0.12.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "h2 0.4.10", - "http 1.3.1", - "http-body 1.0.1", - "http-body-util", - "hyper 1.6.0", - "hyper-rustls", - "hyper-tls", - "hyper-util", - "js-sys", - "log", - "mime", - "native-tls", - "percent-encoding", - "pin-project-lite", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-native-tls", - "tower 0.5.2", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "ring" version = "0.17.14" @@ -2108,204 +1014,67 @@ dependencies = [ ] [[package]] -name = "rocket" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a516907296a31df7dc04310e7043b61d71954d703b603cc6867a026d7e72d73f" -dependencies = [ - "async-stream", - "async-trait", - "atomic 0.5.3", - "binascii", - "bytes", - "either", - "figment", - "futures", - "indexmap 2.9.0", - "log", - "memchr", - "multer", - "num_cpus", - "parking_lot", - "pin-project-lite", - "rand 0.8.5", - "ref-cast", - "rocket_codegen", - "rocket_http", - "serde", - "serde_json", - "state", - "tempfile", - "time", - "tokio", - "tokio-stream", - "tokio-util", - "ubyte", - "version_check", - "yansi", -] - -[[package]] -name = "rocket_codegen" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "575d32d7ec1a9770108c879fc7c47815a80073f96ca07ff9525a94fcede1dd46" -dependencies = [ - "devise", - "glob", - "indexmap 2.9.0", - "proc-macro2", - "quote", - "rocket_http", - "syn 2.0.102", - "unicode-xid", - "version_check", -] - -[[package]] -name = "rocket_http" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e274915a20ee3065f611c044bd63c40757396b6dbc057d6046aec27f14f882b9" -dependencies = [ - "cookie", - "either", - "futures", - "http 0.2.12", - "hyper 0.14.32", - "indexmap 2.9.0", - "log", - "memchr", - "pear", - "percent-encoding", - "pin-project-lite", - "ref-cast", - "serde", - "smallvec", - "stable-pattern", - "state", - "time", - "tokio", - "uncased", -] - -[[package]] -name = "rsa" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" -dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core 0.6.4", - "signature", - "spki", - "subtle", - "zeroize", -] - -[[package]] -name = "rust-nex" -version = "0.1.0" +name = "rnex-core" +version = "0.1.1" dependencies = [ "anyhow", - "async-trait", "bytemuck", - "chacha20", + "cfg-if", "chrono", - "ctrlc", "dotenv", - "futures", "hex", "hmac", "json", "log", - "macros", + "macros 0.0.0", "md-5", "once_cell", "paste", - "prost", "rand 0.8.5", "rc4", - "reqwest", - "rocket", - "rsa", - "rustls", - "rustls-pki-types", - "rustls-webpki", "serde", - "sha2", + "serde_json", "simplelog", "thiserror", "tokio", - "tokio-rustls", - "tokio-stream", - "tokio-tungstenite", - "tonic", - "tonic-build", - "tungstenite", "typenum", - "v_byte_macros", + "ureq", + "v-byte-helpers", ] [[package]] name = "rustc-demangle" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustix" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", -] +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustls" -version = "0.23.27" +version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ - "aws-lc-rs", "log", "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pki-types" version = "1.12.0" @@ -2317,11 +1086,10 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ - "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -2333,27 +1101,15 @@ version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -2362,9 +1118,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "2.11.1" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ "bitflags", "core-foundation", @@ -2375,9 +1131,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -2385,55 +1141,45 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serde_spanned" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", "serde", + "serde_core", + "zmij", ] [[package]] @@ -2447,26 +1193,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - [[package]] name = "shlex" version = "1.3.0" @@ -2483,14 +1209,10 @@ dependencies = [ ] [[package]] -name = "signature" -version = "2.2.0" +name = "simd-adler32" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core 0.6.4", -] +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "simplelog" @@ -2505,12 +1227,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "smallvec" @@ -2520,53 +1239,19 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.5.10" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "stable-pattern" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4564168c00635f88eaed410d5efa8131afa8d8699a612c80c455a0ba05c21045" -dependencies = [ - "memchr", + "windows-sys 0.59.0", ] [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "state" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" -dependencies = [ - "loom", -] +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "subtle" @@ -2587,24 +1272,15 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.102" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6397daf94fa90f058bd0fd88429dd9e5738999cca8d701813c80723add80462" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - [[package]] name = "synstructure" version = "0.13.2" @@ -2613,41 +1289,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", -] - -[[package]] -name = "system-configuration" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" -dependencies = [ - "bitflags", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tempfile" -version = "3.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" -dependencies = [ - "fastrand", - "getrandom 0.3.3", - "once_cell", - "rustix 1.0.7", - "windows-sys 0.59.0", + "syn 2.0.104", ] [[package]] @@ -2676,17 +1318,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", -] - -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", + "syn 2.0.104", ] [[package]] @@ -2724,9 +1356,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -2734,19 +1366,22 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.1" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", + "parking_lot", "pin-project-lite", "signal-hook-registry", + "slab", "socket2", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2757,294 +1392,49 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", + "syn 2.0.104", ] [[package]] name = "tokio-rustls" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", ] -[[package]] -name = "tokio-stream" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-tungstenite" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489a59b6730eda1b0171fcfda8b121f4bee2b35cba8645ca35c5f7ba3eb736c1" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" dependencies = [ "futures-util", "log", + "rustls", + "rustls-native-certs", + "rustls-pki-types", "tokio", + "tokio-rustls", "tungstenite", ] -[[package]] -name = "tokio-util" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap 2.9.0", - "serde", - "serde_spanned", - "toml_datetime", - "toml_write", - "winnow", -] - -[[package]] -name = "toml_write" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" - -[[package]] -name = "tonic" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" -dependencies = [ - "async-stream", - "async-trait", - "axum", - "base64", - "bytes", - "h2 0.4.10", - "http 1.3.1", - "http-body 1.0.1", - "http-body-util", - "hyper 1.6.0", - "hyper-timeout", - "hyper-util", - "percent-encoding", - "pin-project", - "prost", - "socket2", - "tokio", - "tokio-stream", - "tower 0.4.13", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tonic-build" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" -dependencies = [ - "prettyplease", - "proc-macro2", - "prost-build", - "prost-types", - "quote", - "syn 2.0.102", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "indexmap 1.9.3", - "pin-project", - "pin-project-lite", - "rand 0.8.5", - "slab", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" -dependencies = [ - "bitflags", - "bytes", - "futures-util", - "http 1.3.1", - "http-body 1.0.1", - "iri-string", - "pin-project-lite", - "tower 0.5.2", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.102", -] - -[[package]] -name = "tracing-core" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - [[package]] name = "tungstenite" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", - "http 1.3.1", + "http", "httparse", "log", - "rand 0.9.1", + "rand 0.9.2", + "rustls", + "rustls-pki-types", "sha1", "thiserror", "utf-8", @@ -3056,43 +1446,50 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" -[[package]] -name = "ubyte" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f720def6ce1ee2fc44d40ac9ed6d3a59c361c80a75a7aa8e75bb9baed31cf2ea" -dependencies = [ - "serde", -] - -[[package]] -name = "uncased" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" -dependencies = [ - "serde", - "version_check", -] - [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "ureq" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d39cb1dbab692d82a977c0392ffac19e188bd9186a9f32806f0aaa859d75585a" +dependencies = [ + "base64", + "cookie_store", + "flate2", + "log", + "percent-encoding", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "ureq-proto", + "utf-8", + "webpki-roots", +] + +[[package]] +name = "ureq-proto" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b4531c118335662134346048ddb0e54cc86bd7e81866757873055f0e38f5d2" +dependencies = [ + "base64", + "http", + "httparse", + "log", +] + [[package]] name = "url" version = "2.5.4" @@ -3117,26 +1514,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] -name = "v_byte_macros" -version = "0.0.3" -source = "git+https://github.com/DJMrTV/VByteMacros#1f2d596271933155a405ecdd362f750350960d44" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "valuable" +name = "v-byte-helpers" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +source = "git+https://github.com/RusticMaple/VByteMacros#e2f31bded8c5591e847ba03faf79ae0351e43e69" +dependencies = [ + "bytemuck", + "macros 0.1.1", +] [[package]] name = "version_check" @@ -3144,15 +1528,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -3160,12 +1535,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" +name = "wasip2" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] @@ -3190,23 +1565,10 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" -dependencies = [ - "cfg-if", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" version = "0.2.100" @@ -3225,7 +1587,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3240,43 +1602,14 @@ dependencies = [ ] [[package]] -name = "web-sys" -version = "0.3.77" +name = "webpki-roots" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" dependencies = [ - "js-sys", - "wasm-bindgen", + "rustls-pki-types", ] -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix 0.38.44", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - [[package]] name = "winapi-util" version = "0.1.9" @@ -3286,21 +1619,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-core" version = "0.61.2" @@ -3309,7 +1627,7 @@ checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", - "windows-link", + "windows-link 0.1.3", "windows-result", "windows-strings", ] @@ -3322,7 +1640,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", ] [[package]] @@ -3333,25 +1651,20 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", ] [[package]] name = "windows-link" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3bfe459f85da17560875b8bf1423d6f113b7a87a5d942e7da0ac71be7c61f8b" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] -name = "windows-registry" -version = "0.5.2" +name = "windows-link" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bab093bdd303a1240bb99b8aba8ea8a69ee19d34c9e2ef9594e708a4878820" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-result" @@ -3359,7 +1672,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -3368,7 +1681,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -3377,7 +1690,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -3386,22 +1699,16 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "windows-sys" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-link 0.2.1", ] [[package]] @@ -3410,158 +1717,58 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows-targets" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30357ec391cde730f8fbfcdc29adc47518b06504528df977ab5af02ef23fdee9" -dependencies = [ - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -3569,51 +1776,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "windows_x86_64_msvc" -version = "0.53.0" +name = "wit-bindgen" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - -[[package]] -name = "winnow" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" -dependencies = [ - "memchr", -] - -[[package]] -name = "wit-bindgen-rt" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags", -] +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" - -[[package]] -name = "yansi" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" -dependencies = [ - "is-terminal", -] +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -3621,41 +1800,41 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", ] [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" dependencies = [ "zerofrom-derive", ] @@ -3668,7 +1847,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", "synstructure", ] @@ -3680,9 +1859,9 @@ checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -3691,9 +1870,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -3702,11 +1881,17 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", ] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index dd12b89..046a20f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,91 +1,8 @@ -[package] -name = "rust-nex" -version = "0.1.0" -edition = "2021" - -[profile.prod] -inherits = "release" -overflow-checks = false -strip = true -debug = false -debug-assertions = false -lto = true -incremental = false - - - -[dependencies] -bytemuck = { version = "1.21.0", features = ["derive"] } -dotenv = "0.15.0" -once_cell = "1.20.2" -rc4 = "0.1.0" -thiserror = "2.0.11" -v_byte_macros = { git = "https://github.com/DJMrTV/VByteMacros" } -simplelog = "0.12.2" -chrono = "0.4.39" -log = "0.4.25" -anyhow = "1.0.95" -rand = "0.8.5" - -hmac = "0.12.1" -md-5 = "^0.10.6" -tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread", "net", "sync", "fs"] } -tokio-stream = { version = "0.1.17", features = ["io-util"] } -tonic = "0.12.3" -prost = "0.13.4" -hex = "0.4.3" - -macros = { path = "macros" } -rocket = { version = "0.5.1", features = ["json", "serde_json"] } -serde = { version = "1.0.217", features = ["derive"] } -async-trait = "0.1.86" -paste = "1.0.15" -typenum = "1.18.0" -futures = "0.3.31" -reqwest = "0.12.18" -json = "0.12.4" -ctrlc = "3.4.7" -rsa = "0.9.8" -sha2 = "0.10.9" -chacha20 = "0.9.1" - -rustls = "0.23.27" - -rustls-pki-types = "1.12.0" -rustls-webpki = "0.103.3" -tokio-rustls = "0.26.2" -tokio-tungstenite = "0.27.0" -tungstenite = "0.27.0" - - - - -[build-dependencies] -tonic-build = "0.12.3" - -[features] -default = ["secure", "auth"] -secure = [] -auth = [] -no_tls = [] - -[[bin]] -name = "proxy_insecure" -path = "src/executables/proxy_insecure.rs" - -[[bin]] -name = "proxy_secure" -path = "src/executables/proxy_secure.rs" - -[[bin]] -name = "backend_server_insecure" -path = "src/executables/backend_server_insecure.rs" - - -[[bin]] -name = "backend_server_secure" -path = "src/executables/backend_server_secure.rs" - -[[bin]] -name = "edge_node_holder_server" -path = "src/executables/edge_node_holder_server.rs" \ No newline at end of file +[workspace] +resolver = "3" +members = [ + "macros", + "rnex-core", + "prudpv1", + "prudpv0" +, "proxy", "proxy-common", "prudplite"] diff --git a/Dockerfile b/Dockerfile index 74f7efd..e181a93 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,42 @@ -FROM rust:alpine AS builder +FROM rust:alpine AS build-container + +RUN apk add --no-cache protobuf-dev git musl-dev lld openssl-dev openssl-libs-static yq bash + +FROM build-container AS builder WORKDIR /app COPY . . -RUN apk add --no-cache protobuf-dev git musl-dev lld openssl-dev openssl-libs-static +ARG EDITION RUN git submodule update --init --recursive -RUN OPENSSL_LIB_DIR=/usr/lib OPENSSL_INCLUDE_DIR=/usr/include/openssl OPENSSL_STATIC=1 RUSTFLAGS="-C relocation-model=static -C linker=ld.lld" cargo test --target x86_64-unknown-linux-musl -RUN OPENSSL_LIB_DIR=/usr/lib OPENSSL_INCLUDE_DIR=/usr/include/openssl OPENSSL_STATIC=1 RUSTFLAGS="-C relocation-model=static -C linker=ld.lld" cargo build --profile prod --target x86_64-unknown-linux-musl +RUN ./test-edition.sh +RUN ./build-edition.sh -FROM scratch AS final +FROM scratch AS node-holder +COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/edge_node_holder_server /edge_node_holder_server +ENTRYPOINT ["/edge_node_holder_server"] -# Copy the compiled binary from the builder stage -COPY --from=builder /app/target/x86_64-unknown-linux-musl/prod/splatoon-server-rust /splatoon-server-rust -# Command to run the application -ENTRYPOINT ["/splatoon-server-rust"] +FROM scratch AS proxy-insecure +COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/proxy_insecure /proxy_insecure +ENTRYPOINT ["/proxy_insecure"] + +FROM scratch AS proxy-secure +COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/proxy_secure /proxy_secure +ENTRYPOINT ["/proxy_secure"] + + +FROM scratch AS backend-auth +COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/backend_server_insecure /backend_server_insecure +ENTRYPOINT ["/backend_server_insecure"] + +FROM scratch AS backend-secure +COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/backend_server_secure /backend_server_secure +ENTRYPOINT ["/backend_server_secure"] + +# make sure the final output container is the dev container so that we can use it from the devcontainer.json +FROM build-container AS dev-container +RUN apk add openjdk21-jdk gcompat diff --git a/README.md b/README.md index 4900bd8..c5f1ada 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,17 @@ - Pretendo team for the rest of the Servers and Reverse engineering efforts - Kinnay for his huge work on reversing nex servers and documentation(https://github.com/Kinnay/NintendoClients/) - Splatfestival testing team for helping us test our messes of code -- The SPFN team(Andrea and DJMrTV) \ No newline at end of file +- 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. diff --git a/build-edition.sh b/build-edition.sh new file mode 100755 index 0000000..5d65fc3 --- /dev/null +++ b/build-edition.sh @@ -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 diff --git a/buildscripts/common.sh b/buildscripts/common.sh new file mode 100755 index 0000000..8c5e67e --- /dev/null +++ b/buildscripts/common.sh @@ -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" diff --git a/editions.yaml b/editions.yaml new file mode 100644 index 0000000..7a6869c --- /dev/null +++ b/editions.yaml @@ -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" diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..7826732 --- /dev/null +++ b/flake.lock @@ -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 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..0863018 --- /dev/null +++ b/flake.nix @@ -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; }; + }; + }; +} diff --git a/grpc-protobufs b/grpc-protobufs deleted file mode 160000 index 405fe9b..0000000 --- a/grpc-protobufs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 405fe9b47b416e76b21d7087b2ed11606deccfcf diff --git a/macros/Cargo.lock b/macros/Cargo.lock index a82b7df..acad79d 100644 --- a/macros/Cargo.lock +++ b/macros/Cargo.lock @@ -2,15 +2,55 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + [[package]] name = "macros" version = "0.0.0" dependencies = [ "proc-macro2", "quote", + "rand", "syn", ] +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.93" @@ -29,6 +69,41 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + [[package]] name = "syn" version = "2.0.98" @@ -45,3 +120,41 @@ name = "unicode-ident" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/macros/Cargo.toml b/macros/Cargo.toml index e301949..f748300 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -1,17 +1,16 @@ [package] name = "macros" version = "0.0.0" -authors = ["DJMrTV "] +authors = ["RusticMaple "] description = "A `cargo generate` template for quick-starting a procedural macro crate" keywords = ["template", "proc_macro", "procmacro"] edition = "2018" [lib] proc-macro = true +doctest = false [dependencies] quote = "1.0.38" proc-macro2 = "1.0.93" syn = { version = "2.0.98", features = ["full"] } -rand = "0.9.0" - diff --git a/macros/src/lib.rs b/macros/src/lib.rs index ad77595..432fc03 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -10,8 +10,8 @@ use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{ - parse_macro_input, Attribute, Data, DataStruct, DeriveInput, Fields, FnArg, LitInt, Pat, Token, - TraitItem, + parse_macro_input, Attribute, Data, DataStruct, DeriveInput, Fields, FnArg, Lit, LitInt, + LitStr, Pat, Token, TraitItem, }; struct ProtoInputParams { @@ -48,7 +48,11 @@ impl Parse for ProtoInputParams { fn gen_serialize_data_struct( s: DataStruct, struct_attr: Option<&Attribute>, -) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) { +) -> ( + proc_macro2::TokenStream, + proc_macro2::TokenStream, + proc_macro2::TokenStream, +) { let serialize_base_content = { let mut serialize_content = quote! {}; @@ -65,7 +69,7 @@ fn gen_serialize_data_struct( let ident = f.ident.as_ref().unwrap(); serialize_content.append_all(quote! { - self.#ident.serialize(writer)?; + rnex_core::rmc::structures::RmcSerialize::serialize(&self.#ident, writer)?; }) } @@ -119,6 +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 let serialize_base_content = if let Some(attr) = struct_attr { @@ -143,9 +166,18 @@ fn gen_serialize_data_struct( quote! { #pre_inner - rust_nex::rmc::structures::rmc_struct::write_struct(writer, #version, |mut writer|{ - #serialize_base_content - })?; + rnex_core::rmc::structures::rmc_struct::write_struct( + writer, + #version, + rnex_core::rmc::structures::helpers::len_of_write( + |writer|{ + #serialize_base_content + } + ), + |writer|{ + #serialize_base_content + } + )?; Ok(()) } @@ -176,7 +208,7 @@ fn gen_serialize_data_struct( quote! { #pre_inner - Ok(rust_nex::rmc::structures::rmc_struct::read_struct(reader, #version, move |mut reader|{ + Ok(rnex_core::rmc::structures::rmc_struct::read_struct(reader, #version, move |mut reader|{ #deserialize_base_content })?) } @@ -184,7 +216,13 @@ fn gen_serialize_data_struct( deserialize_base_content }; - (serialize_base_content, deserialize_base_content) + let write_size = quote! { + fn serialize_write_size(&self) -> rnex_core::rmc::structures::Result{ + Ok(#write_size) + } + }; + + (serialize_base_content, deserialize_base_content, write_size) } #[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"); };*/ - let (serialize_base_content, deserialize_base_content) = match derive_input.data { + let (serialize_base_content, deserialize_base_content, write_size) = match derive_input.data { Data::Struct(s) => gen_serialize_data_struct(s, struct_attr), Data::Enum(e) => { let Some(repr_attr) = repr_attr else { @@ -221,6 +259,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { let mut inner_match_de = quote! {}; let mut inner_match_se = quote! {}; + //let mut inner_match_len = quote!{}; for variant in e.variants { let Some((_, val)) = variant.discriminant else { @@ -235,7 +274,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { let name = &field.ident; base.append_all(quote!{ - #name: <#ty as rust_nex::rmc::structures::RmcSerialize>::deserialize(reader)?, + #name: <#ty as rnex_core::rmc::structures::RmcSerialize>::deserialize(reader)?, }); } @@ -248,7 +287,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { let ty = &field.ty; base.append_all(quote!{ - <#ty as rust_nex::rmc::structures::RmcSerialize>::deserialize(reader)?, + <#ty as rnex_core::rmc::structures::RmcSerialize>::deserialize(reader)?, }); } @@ -260,7 +299,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { }; let mut se_with_fields = quote! { - <#ty as rust_nex::rmc::structures::RmcSerialize>::serialize(&#val, writer)?; + <#ty as rnex_core::rmc::structures::RmcSerialize>::serialize(&#val, writer)?; }; match &variant.fields { @@ -270,7 +309,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { let name = &field.ident; se_with_fields.append_all(quote!{ - <#ty as rust_nex::rmc::structures::RmcSerialize>::serialize(#name ,writer)?; + <#ty as rnex_core::rmc::structures::RmcSerialize>::serialize(#name ,writer)?; }); } } @@ -281,7 +320,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { let ident = Ident::new(&format!("val_{}", i), Span::call_site()); se_with_fields.append_all(quote!{ - <#ty as rust_nex::rmc::structures::RmcSerialize>::serialize(#ident, writer)?; + <#ty as rnex_core::rmc::structures::RmcSerialize>::serialize(#ident, writer)?; }); } } @@ -344,15 +383,15 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { }; let deserialize_base_content = quote! { - let val: Self = match <#ty as rust_nex::rmc::structures::RmcSerialize>::deserialize(reader)?{ + let val: Self = match <#ty as rnex_core::rmc::structures::RmcSerialize>::deserialize(reader)?{ #inner_match_de - v => return Err(rust_nex::rmc::structures::Error::UnexpectedValue(v as _)) + v => return Err(rnex_core::rmc::structures::Error::UnexpectedValue(v as _)) }; Ok(val) }; - (serialize_base_content, deserialize_base_content) + (serialize_base_content, deserialize_base_content, quote! {}) } Data::Union(_) => { unimplemented!() @@ -361,18 +400,27 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { // 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 tokens = quote! { - impl rust_nex::rmc::structures::RmcSerialize for #ident{ - fn serialize(&self, writer: &mut dyn ::std::io::Write) -> rust_nex::rmc::structures::Result<()>{ + impl rnex_core::rmc::structures::RmcSerialize for #ident{ + #[inline(always)] + fn serialize(&self, writer: &mut impl ::std::io::Write) -> rnex_core::rmc::structures::Result<()>{ #serialize_base_content - - + } + #[inline(always)] + fn deserialize(reader: &mut impl ::std::io::Read) -> rnex_core::rmc::structures::Result{ + #deserialize_base_content } - fn deserialize(reader: &mut dyn ::std::io::Read) -> rust_nex::rmc::structures::Result{ - #deserialize_base_content + #write_size + + 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{ - async fn rmc_call(&self, remote_response_connection: &rust_nex::util::SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec){ + impl rnex_core::rmc::protocols::RmcCallable for #struct_name{ + async fn rmc_call(&self, remote_response_connection: &rnex_core::util::SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec){ ::rmc_call(self, remote_response_connection, protocol_id, method_id, call_id, rest).await; } } diff --git a/macros/src/protos.rs b/macros/src/protos.rs index 14ec2f6..2e95e71 100644 --- a/macros/src/protos.rs +++ b/macros/src/protos.rs @@ -1,46 +1,46 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote, ToTokens}; -use syn::{LitInt, LitStr, ReturnType, Type}; use syn::token::{Brace, Paren, Semi}; +use syn::{Attribute, LitInt, LitStr, ReturnType, Type}; -pub struct ProtoMethodData{ +pub struct ProtoMethodData { pub id: LitInt, pub name: Ident, pub parameters: Vec<(Ident, Type)>, pub ret_val: ReturnType, } - /// This is a representation of the code generated by `rmc_proto` it serves to split the logic of /// acquiring data from the actual generation to tidy up the process into first getting then /// generating. /// /// Use the [`ToTokens`] trait to generate the actual code. -pub struct RmcProtocolData{ +pub struct RmcProtocolData { pub has_returns: bool, pub id: LitInt, pub name: Ident, - pub methods: Vec + pub methods: Vec, } -impl RmcProtocolData{ - fn generate_raw_trait(&self, tokens: &mut TokenStream){ - let Self{ +impl RmcProtocolData { + fn generate_raw_trait(&self, tokens: &mut TokenStream) { + let Self { has_returns, name, id, - methods + methods, } = self; // this gives us the name which the identifier of the corresponding Raw trait let raw_name = Ident::new(&format!("Raw{}", name), name.span()); - // boilerplate tokens which all raw traits need - quote!{ + quote! { #[doc(hidden)] + #[allow(unused_must_use)] pub trait #raw_name: #name - }.to_tokens(tokens); + } + .to_tokens(tokens); // generate the body of the raw protocol trait Brace::default().surround(tokens, |tokens|{ @@ -54,6 +54,7 @@ impl RmcProtocolData{ let raw_name = Ident::new(&format!("raw_{}", name), name.span()); quote!{ + #[inline(always)] async fn #raw_name }.to_tokens(tokens); @@ -73,7 +74,7 @@ impl RmcProtocolData{ for (param_name, param_type) in parameters{ quote!{ let Ok(#param_name) = - <#param_type as rust_nex::rmc::structures::RmcSerialize>::deserialize( + <#param_type as rnex_core::rmc::structures::RmcSerialize>::deserialize( &mut cursor ) else }.to_tokens(tokens); @@ -84,7 +85,7 @@ impl RmcProtocolData{ quote! { { log::error!(#error_msg); - return Err(rust_nex::rmc::response::ErrorCode::Core_InvalidArgument); + return Err(rnex_core::rmc::response::ErrorCode::Core_InvalidArgument); }; }.to_tokens(tokens) } else { @@ -116,7 +117,7 @@ impl RmcProtocolData{ quote!{ let retval = retval?; let mut vec = Vec::new(); - rust_nex::rmc::structures::RmcSerialize::serialize(&retval, &mut vec).ok(); + rnex_core::rmc::structures::RmcSerialize::serialize(&retval, &mut vec).ok(); Ok(vec) }.to_tokens(tokens); } @@ -124,9 +125,10 @@ impl RmcProtocolData{ } quote!{ + #[inline(always)] async fn rmc_call_proto( &self, - remote_response_connection: &rust_nex::util::SendingBufferConnection, + remote_response_connection: &rnex_core::util::SendingBufferConnection, method_id: u32, call_id: u32, data: Vec, @@ -165,7 +167,7 @@ impl RmcProtocolData{ }.to_tokens(tokens); if self.has_returns { quote! { - Err(rust_nex::rmc::response::ErrorCode::Core_NotImplemented) + Err(rnex_core::rmc::response::ErrorCode::Core_NotImplemented) }.to_tokens(tokens); } }); @@ -176,7 +178,7 @@ impl RmcProtocolData{ if *has_returns{ quote!{ - rust_nex::rmc::response::send_result( + rnex_core::rmc::response::send_result( remote_response_connection, ret, #id, @@ -188,9 +190,10 @@ impl RmcProtocolData{ }); }); - quote!{ + quote! { impl #raw_name for T{} - }.to_tokens(tokens); + } + .to_tokens(tokens); } 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 let remote_name = Ident::new(&format!("Remote{}", name), name.span()); - // boilerplate tokens which all raw traits need - quote!{ + quote! { #[doc(hidden)] - pub trait #remote_name: rust_nex::rmc::protocols::HasRmcConnection - }.to_tokens(tokens); + #[allow(unused_must_use)] + pub trait #remote_name: rnex_core::rmc::protocols::HasRmcConnection + } + .to_tokens(tokens); // generate the body of the raw protocol trait Brace::default().surround(tokens, |tokens|{ @@ -247,12 +251,12 @@ impl RmcProtocolData{ for (param_name, param_type) in parameters{ quote!{ - rust_nex::result::ResultExtension::display_err_or_some( - <#param_type as rust_nex::rmc::structures::RmcSerialize>::serialize( + rnex_core::result::ResultExtension::display_err_or_some( + <#param_type as rnex_core::rmc::structures::RmcSerialize>::serialize( &#param_name, &mut cursor ) - ).ok_or(rust_nex::rmc::response::ErrorCode::Core_InvalidArgument) + ).ok_or(rnex_core::rmc::response::ErrorCode::Core_InvalidArgument) }.to_tokens(tokens); if self.has_returns { quote! { @@ -268,25 +272,25 @@ impl RmcProtocolData{ quote!{ let call_id = rand::random(); - let message = rust_nex::rmc::message::RMCMessage{ + let message = rnex_core::rmc::message::RMCMessage{ call_id, method_id: #method_id, protocol_id: #proto_id, rest_of_data: send_data }; - let rmc_conn = ::get_connection(self); + let rmc_conn = ::get_connection(self); }.to_tokens(tokens); if *has_returns{ quote!{ - rust_nex::result::ResultExtension::display_err_or_some( + rnex_core::result::ResultExtension::display_err_or_some( rmc_conn.make_raw_call(&message).await - ).ok_or(rust_nex::rmc::response::ErrorCode::Core_Exception) + ).ok_or(rnex_core::rmc::response::ErrorCode::Core_Exception) }.to_tokens(tokens); } else { quote!{ - rust_nex::result::ResultExtension::display_err_or_some( + rnex_core::result::ResultExtension::display_err_or_some( rmc_conn.make_raw_call_no_response(&message).await ); }.to_tokens(tokens); @@ -295,40 +299,29 @@ impl RmcProtocolData{ }) } }); - - } - fn generate_raw_info(&self, tokens: &mut TokenStream){ - let Self{ - name, - id, - .. - } = self; + fn generate_raw_info(&self, tokens: &mut TokenStream) { + let Self { name, id, .. } = self; let raw_info_name = Ident::new(&format!("Raw{}Info", name), Span::call_site()); - - quote!{ + quote! { #[doc(hidden)] pub struct #raw_info_name; impl #raw_info_name { 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) { self.generate_raw_trait(tokens); self.generate_raw_info(tokens); self.generate_raw_remote_trait(tokens); - - - - } } - diff --git a/out/backend_server_secure b/out/backend_server_secure new file mode 100755 index 0000000..be14059 Binary files /dev/null and b/out/backend_server_secure differ diff --git a/perf.data b/perf.data new file mode 100644 index 0000000..2cf011f Binary files /dev/null and b/perf.data differ diff --git a/proxy-common/Cargo.toml b/proxy-common/Cargo.toml new file mode 100644 index 0000000..bd13ffa --- /dev/null +++ b/proxy-common/Cargo.toml @@ -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" diff --git a/proxy-common/src/lib.rs b/proxy-common/src/lib.rs new file mode 100644 index 0000000..5cc13b7 --- /dev/null +++ b/proxy-common/src/lib.rs @@ -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, Box), +} +impl Into 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(name: &'static str) -> Result +where + (&'static str, T::Err): Into, +{ + 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 = + LazyLock::new(|| VirtualPort::parse(env!("RNEX_VIRTUAL_PORT_INSECURE")).unwrap()); +const VIRTUAL_PORT_SECURE: LazyLock = + LazyLock::new(|| VirtualPort::parse(env!("RNEX_VIRTUAL_PORT_SECURE")).unwrap()); +impl ProxyStartupParam { + pub fn new(prox_ty: ProxyType) -> Result { + 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, Option); +impl Deref for OnRemoteDrop { + 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 + OnRemoteDrop +{ + 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 RmcCallable + for OnRemoteDrop +{ + fn rmc_call( + &self, + _responder: &SendingBufferConnection, + _protocol_id: u16, + _method_id: u32, + _call_id: u32, + _rest: Vec, + ) -> impl Future + Send { + // maybe respond with not implemented or something + async {} + } +} + +impl Drop for OnRemoteDrop { + 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::::new( + r, + shutdown_callback, + )) + }); +} + +pub async fn new_backend_connection( + param: &ProxyStartupParam, + addr: PRUDPSockAddr, + pid: PID, +) -> Option { + 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); + } +} diff --git a/proxy/Cargo.toml b/proxy/Cargo.toml new file mode 100644 index 0000000..98e8b2b --- /dev/null +++ b/proxy/Cargo.toml @@ -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" diff --git a/proxy/src/insecure.rs b/proxy/src/insecure.rs new file mode 100644 index 0000000..1451a64 --- /dev/null +++ b/proxy/src/insecure.rs @@ -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; +} diff --git a/proxy/src/lib.rs b/proxy/src/lib.rs new file mode 100644 index 0000000..ea5b02c --- /dev/null +++ b/proxy/src/lib.rs @@ -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() +} diff --git a/proxy/src/secure.rs b/proxy/src/secure.rs new file mode 100644 index 0000000..6d42c87 --- /dev/null +++ b/proxy/src/secure.rs @@ -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; +} diff --git a/prudplite/Cargo.toml b/prudplite/Cargo.toml new file mode 100644 index 0000000..902b1a7 --- /dev/null +++ b/prudplite/Cargo.toml @@ -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" } diff --git a/prudplite/src/crypto/insecure.rs b/prudplite/src/crypto/insecure.rs new file mode 100644 index 0000000..34d0f93 --- /dev/null +++ b/prudplite/src/crypto/insecure.rs @@ -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)> { + Some((100, vec![])) + } + fn new() -> Self { + Self + } +} diff --git a/prudplite/src/crypto/mod.rs b/prudplite/src/crypto/mod.rs new file mode 100644 index 0000000..38d8091 --- /dev/null +++ b/prudplite/src/crypto/mod.rs @@ -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)>; + fn new() -> Self; +} diff --git a/prudplite/src/crypto/secure.rs b/prudplite/src/crypto/secure.rs new file mode 100644 index 0000000..4aff759 --- /dev/null +++ b/prudplite/src/crypto/secure.rs @@ -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)> { + 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) + } +} diff --git a/prudplite/src/lib.rs b/prudplite/src/lib.rs new file mode 100644 index 0000000..05938d7 --- /dev/null +++ b/prudplite/src/lib.rs @@ -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, + active: bool, + websocket: WebSocketStream, + pid: PID, + backend_conn: SplittableBufferConnection, + addr: PRUDPSockAddr, + incoming_reliable: HashMap>, + client_reliable_counter: u16, + server_reliable_counter: u16, +} + +impl ConnectionState { + pub async fn handle_incoming_prudp(&mut self, packet: LitePacket, 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( + param: Arc, + crypto: Arc, + 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(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::(param).await; +} + +pub async fn start_insecure(param: ProxyStartupParam) { + start_proxy::(param).await; +} diff --git a/prudplite/src/main.rs b/prudplite/src/main.rs new file mode 100644 index 0000000..b4d8a0e --- /dev/null +++ b/prudplite/src/main.rs @@ -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); +} diff --git a/prudplite/src/packet.rs b/prudplite/src/packet.rs new file mode 100644 index 0000000..bd8d87c --- /dev/null +++ b/prudplite/src/packet.rs @@ -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 { + 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); + +pub struct PacketSpecificIter<'a>(Cursor<&'a [u8]>); + +impl<'a> Iterator for PacketSpecificIter<'a> { + type Item = PacketSpecificData; + + fn next(&mut self) -> Option { + PacketSpecificData::consume(&mut self.0).ok() + } +} + +impl> LitePacket { + 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::())?).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::())?).ok() + } + + pub fn payload(&self) -> Option<&[u8]> { + let header = self.header()?; + self.0 + .as_ref() + .get(size_of::() + 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::() + len as usize..) + } + + pub fn packet_specific_raw(&self) -> Option<&[u8]> { + let header = self.header()?; + self.0.as_ref().get( + size_of::() + ..size_of::() + 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::()..size_of::() + len as usize) + } + + pub fn packet_specific_iter(&self) -> Option { + self.packet_specific_raw() + .map(Cursor::new) + .map(PacketSpecificIter) + } +} + +pub fn create_packet_from( + header: LiteHeader, + specific_data: &[PacketSpecificData], + data: &[u8], +) -> Vec { + let specific_size: usize = specific_data.iter().map(|v| v.write_size()).sum(); + let mut packet = LitePacket::new(vec![ + 0u8; + size_of::() + 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 + } +} diff --git a/prudpv0/Cargo.toml b/prudpv0/Cargo.toml new file mode 100644 index 0000000..447436d --- /dev/null +++ b/prudpv0/Cargo.toml @@ -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"] diff --git a/prudpv0/src/crypto/common_crypto.rs b/prudpv0/src/crypto/common_crypto.rs new file mode 100644 index 0000000..7196638 --- /dev/null +++ b/prudpv0/src/crypto/common_crypto.rs @@ -0,0 +1,54 @@ +trait IterExtra: Iterator { + fn sum_wrapping_u8(&mut self) -> u8 + where + Self::Item: Into; + + fn sum_wrapping_u32(&mut self) -> u32 + where + Self::Item: Into; +} + +impl IterExtra for T { + fn sum_wrapping_u8(&mut self) -> u8 + where + Self::Item: Into, + { + 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, + { + 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 +} diff --git a/prudpv0/src/crypto/friends_common.rs b/prudpv0/src/crypto/friends_common.rs new file mode 100644 index 0000000..a8f1629 --- /dev/null +++ b/prudpv0/src/crypto/friends_common.rs @@ -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; diff --git a/prudpv0/src/crypto/friends_insecure.rs b/prudpv0/src/crypto/friends_insecure.rs new file mode 100644 index 0000000..1f05681 --- /dev/null +++ b/prudpv0/src/crypto/friends_insecure.rs @@ -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>, + 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 = ::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)> { + Some(( + InsecureInstance { + pair: EncryptionPair::init_both(|| Rc4::new(&DEFAULT_KEY)), + self_signat, + remote_signat, + }, + vec![], + )) + } +} diff --git a/prudpv0/src/crypto/friends_secure.rs b/prudpv0/src/crypto/friends_secure.rs new file mode 100644 index 0000000..acfa3bc --- /dev/null +++ b/prudpv0/src/crypto/friends_secure.rs @@ -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>, + 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 = ::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)> { + 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, + )) + } +} diff --git a/prudpv0/src/crypto/insecure.rs b/prudpv0/src/crypto/insecure.rs new file mode 100644 index 0000000..5d3dff3 --- /dev/null +++ b/prudpv0/src/crypto/insecure.rs @@ -0,0 +1,9 @@ +use crate::crypto::Crypto; + +pub struct Insecure(); + +impl Crypto for Insecure { + fn calculate_checksum(&self, data: &[u8]) -> u8 { + todo!() + } +} diff --git a/prudpv0/src/crypto/mod.rs b/prudpv0/src/crypto/mod.rs new file mode 100644 index 0000000..f4806f6 --- /dev/null +++ b/prudpv0/src/crypto/mod.rs @@ -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)>; +} + +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::*; + } +} diff --git a/prudpv0/src/crypto/secure.rs b/prudpv0/src/crypto/secure.rs new file mode 100644 index 0000000..435986e --- /dev/null +++ b/prudpv0/src/crypto/secure.rs @@ -0,0 +1,9 @@ +use crate::crypto::Crypto; + +pub struct Secure(); + +impl Crypto for Secure { + fn calculate_checksum(&self, data: &[u8]) -> u8 { + todo!() + } +} diff --git a/prudpv0/src/lib.rs b/prudpv0/src/lib.rs new file mode 100644 index 0000000..444899a --- /dev/null +++ b/prudpv0/src/lib.rs @@ -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 = 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 = 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(param: ProxyStartupParam) { + info!("creating cryptography instance"); + let mut crypto = Arc::new(T::new()); + info!("binding to socket"); + + let server: Arc> = Arc::new(Server::new(param).await); + + info!("waiting on packets"); + server.run_task().await; + } + + pub async fn start_secure(param: ProxyStartupParam) { + start_proxy::(param).await; + } + + pub async fn start_insecure(param: ProxyStartupParam) { + start_proxy::(param).await; + } + } +} diff --git a/prudpv0/src/packet.rs b/prudpv0/src/packet.rs new file mode 100644 index 0000000..50bfe55 --- /dev/null +++ b/prudpv0/src/packet.rs @@ -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>(pub T); + +impl> PRUDPV0Packet { + #[inline(always)] + pub fn get_packet_specific_size(&self) -> Option { + 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::())?).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::())?).ok() + } + + #[inline(always)] + pub fn connection_signature(&self) -> Option<&[u8; 4]> { + let offset = size_of::(); + 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::(); + 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::() + 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::(); + 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::(); + Some(self.0.as_ref().get(offset)?) + } + + #[inline(always)] + fn get_payload_offset(&self) -> Option { + Some(size_of::() + 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 { + 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::() + + (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::() + 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 { + 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 { + 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 { + 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 { + 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 { + 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 +} diff --git a/prudpv0/src/server.rs b/prudpv0/src/server.rs new file mode 100644 index 0000000..899bbd6 --- /dev/null +++ b/prudpv0/src/server.rs @@ -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 { + last_action: Instant, + crypto_instance: C, + server_packet_counter: u16, + client_packet_counter: u16, + unacknowledged_packets: HashMap>>, + packet_queue: HashMap>)>, +} +pub struct Connection { + alive: AtomicBool, + session_id: u8, + target: SendingBufferConnection, + self_signat: [u8; 4], + remote_signat: [u8; 4], + addr: PRUDPSockAddr, + inner: Mutex>, +} + +impl InternalConnection { + 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 { + param: ProxyStartupParam, + socket: UdpSocket, + crypto: C, + connections: RwLock>>>, +} + +impl Server { + async fn send_data_packet(self: Arc, conn: Arc>, 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, + conn: Weak>, + 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, conn: Weak>) { + 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, packet: PRUDPV0Packet>, 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, packet: PRUDPV0Packet>, 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, mut packet: PRUDPV0Packet>, 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, mut packet: PRUDPV0Packet>, 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, + mut packet: PRUDPV0Packet>, + 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>> { + let rd = self.connections.read().await; + let res = rd.get(&addr).cloned(); + drop(rd); + res + } + + async fn process_packet(self: Arc, packet: PRUDPV0Packet>, 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) { + loop { + let mut vec: Vec = 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, + } + } +} diff --git a/prudpv1/Cargo.toml b/prudpv1/Cargo.toml new file mode 100644 index 0000000..b8346bf --- /dev/null +++ b/prudpv1/Cargo.toml @@ -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 = [] diff --git a/prudpv1/src/executables/common.rs b/prudpv1/src/executables/common.rs new file mode 100644 index 0000000..8d6f4c1 --- /dev/null +++ b/prudpv1/src/executables/common.rs @@ -0,0 +1,17 @@ +use once_cell::sync::Lazy; +use std::env; +use std::net::SocketAddrV4; + +pub static EDGE_NODE_HOLDER: Lazy = 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 = Lazy::new(|| { + env::var("FORWARD_DESTINATION") + .ok() + .and_then(|s| s.parse().ok()) + .expect("FORWARD_DESTINATION not set") +}); diff --git a/prudpv1/src/executables/mod.rs b/prudpv1/src/executables/mod.rs new file mode 100644 index 0000000..2c0d6cf --- /dev/null +++ b/prudpv1/src/executables/mod.rs @@ -0,0 +1,3 @@ +pub mod common; +pub mod proxy_insecure; +pub mod proxy_secure; diff --git a/src/executables/proxy_secure.rs b/prudpv1/src/executables/proxy_insecure.rs similarity index 51% rename from src/executables/proxy_secure.rs rename to prudpv1/src/executables/proxy_insecure.rs index 1d554f9..06299d1 100644 --- a/src/executables/proxy_secure.rs +++ b/prudpv1/src/executables/proxy_insecure.rs @@ -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::sync::Arc; use std::time::Duration; -use futures::future::Remote; -use log::{error, warn}; -use macros::rmc_struct; use tokio::net::TcpStream; -use tokio::sync::RwLock; use tokio::task; use tokio::time::sleep; -use tokio_rustls::client::TlsStream; -use tokio_tungstenite::MaybeTlsStream; -use rust_nex::common::setup; -use rust_nex::executables::common::{AUTH_SERVER_ACCOUNT, FORWARD_DESTINATION, OWN_IP_PRIVATE, OWN_IP_PUBLIC, SECURE_EDGE_NODE_HOLDER, SECURE_SERVER_ACCOUNT, SERVER_PORT}; -use rust_nex::prudp::packet::VirtualPort; -use rust_nex::prudp::router::Router; -use rust_nex::prudp::secure::Secure; -use rust_nex::prudp::unsecure::Unsecure; -use rust_nex::reggie::EdgeNodeHolderConnectOption::{DontRegister, Register}; -use rust_nex::rmc::response::ErrorCode; -use rust_nex::rnex_proxy_common::ConnectionInitData; -use rust_nex::reggie::{RemoteEdgeNodeHolder, UnitPacketWrite}; -use rust_nex::rmc::structures::RmcSerialize; -use rust_nex::reggie::UnitPacketRead; -use rust_nex::rmc::protocols::{new_rmc_gateway_connection, OnlyRemote, RemoteInstantiatable}; -use rust_nex::util::SplittableBufferConnection; +use proxy_common::RNEX_ACCESS_KEY; -#[tokio::main] -async fn main() { - setup(); - - let conn = tokio::net::TcpStream::connect(&*SECURE_EDGE_NODE_HOLDER).await.unwrap(); +pub async fn start() { + /*let conn = tokio::net::TcpStream::connect(&*EDGE_NODE_HOLDER) + .await + .unwrap(); let conn: SplittableBufferConnection = conn.into(); - conn.send(Register(SocketAddrV4::new(*OWN_IP_PUBLIC, *SERVER_PORT)).to_data()).await; - - let conn = new_rmc_gateway_connection(conn, |r| Arc::new(OnlyRemote::::new(r))); - + conn.send( + Register(SocketAddrV4::new(*OWN_IP_PUBLIC, *SERVER_PORT)) + .to_data() + .unwrap(), + ) + .await; + let conn = new_rmc_gateway_connection(conn, |r| { + Arc::new(OnlyRemote::::new(r)) + });*/ let (router_secure, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *SERVER_PORT)) .await .expect("unable to start router"); let mut socket_secure = router_secure - .add_socket(VirtualPort::new(1, 10), Secure( - "6f599f81", - SECURE_SERVER_ACCOUNT.clone() - )) + .add_socket(VirtualPort::new(1, 10), Unsecure(RNEX_ACCESS_KEY)) .await .expect("unable to add socket"); @@ -60,8 +57,7 @@ async fn main() { }; task::spawn(async move { - let mut stream - = match TcpStream::connect(*FORWARD_DESTINATION).await { + let mut stream = match TcpStream::connect(*FORWARD_DESTINATION).await { Ok(v) => v, Err(e) => { error!("unable to connect: {}", e); @@ -69,26 +65,31 @@ async fn main() { } }; - if let Err(e) = stream.send_buffer(&ConnectionInitData{ - prudpsock_addr: conn.socket_addr, - pid: conn.user_id - }.to_data()).await{ + if let Err(e) = stream + .send_buffer( + &ConnectionInitData { + prudpsock_addr: conn.socket_addr, + pid: conn.user_id, + } + .to_data() + .unwrap(), + ) + .await + { error!("error connecting to backend: {}", e); return; }; - - loop { tokio::select! { data = conn.recv() => { let Some(data) = data else { - break; + return; }; if let Err(e) = stream.send_buffer(&data[..]).await{ error!("error sending data to backend: {}", e); - break; + return; } }, data = stream.read_buffer() => { @@ -96,10 +97,10 @@ async fn main() { Ok(d) => d, Err(e) => { error!("error reveiving data from backend: {}", e); - break; + return; } }; - + if conn.send(data).await == None{ return; } @@ -111,4 +112,4 @@ async fn main() { } }); } -} \ No newline at end of file +} diff --git a/prudpv1/src/executables/proxy_secure.rs b/prudpv1/src/executables/proxy_secure.rs new file mode 100644 index 0000000..6bf3434 --- /dev/null +++ b/prudpv1/src/executables/proxy_secure.rs @@ -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; + } + } + } + }); + } +} diff --git a/prudpv1/src/lib.rs b/prudpv1/src/lib.rs new file mode 100644 index 0000000..0660a4b --- /dev/null +++ b/prudpv1/src/lib.rs @@ -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; + } + } +} diff --git a/src/prudp/auth_module.rs b/prudpv1/src/prudp/auth_module.rs similarity index 96% rename from src/prudp/auth_module.rs rename to prudpv1/src/prudp/auth_module.rs index d7e93b0..28659d6 100644 --- a/src/prudp/auth_module.rs +++ b/prudpv1/src/prudp/auth_module.rs @@ -1,8 +1,8 @@ +/* use std::net::Ipv4Addr; - pub trait AuthModule{ fn get_auth_key(addr: Ipv4Addr) -> [u8; 32]; -} +}*/ /* struct AuthServerAuthModule; diff --git a/src/prudp/mod.rs b/prudpv1/src/prudp/mod.rs similarity index 71% rename from src/prudp/mod.rs rename to prudpv1/src/prudp/mod.rs index 966523e..7f237c6 100644 --- a/src/prudp/mod.rs +++ b/prudpv1/src/prudp/mod.rs @@ -2,7 +2,5 @@ pub mod packet; pub mod router; pub mod socket; mod auth_module; -pub mod sockaddr; -pub mod station_url; pub mod secure; pub mod unsecure; \ No newline at end of file diff --git a/src/prudp/packet.rs b/prudpv1/src/prudp/packet.rs similarity index 64% rename from src/prudp/packet.rs rename to prudpv1/src/prudp/packet.rs index b66167c..5a27d33 100644 --- a/src/prudp/packet.rs +++ b/prudpv1/src/prudp/packet.rs @@ -3,20 +3,25 @@ // force the compiler to shut up here #![allow(unused_parens)] -use std::fmt::{Debug, Formatter}; -use std::io; -use std::io::{Cursor, Read, Seek, Write}; -use std::net::SocketAddrV4; +use crate::prudp::packet::PacketOption::{ + ConnectionSignature, FragmentId, InitialSequenceId, MaximumSubstreamId, SupportedFunctions, +}; use bytemuck::{Pod, Zeroable}; use hmac::{Hmac, Mac}; 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 v_byte_macros::{SwapEndian}; -use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; -use crate::prudp::packet::flags::ACK; -use crate::prudp::packet::PacketOption::{ConnectionSignature, FragmentId, InitialSequenceId, MaximumSubstreamId, SupportedFunctions}; -use crate::prudp::sockaddr::PRUDPSockAddr; +use v_byte_helpers::SwapEndian; +use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; type Md5Hmac = Hmac; @@ -31,117 +36,11 @@ pub enum Error { #[error("invalid option id {0}")] InvalidOptionId(u8), #[error("option size {size} doesnt match expected option for given option id {id}")] - InvalidOptionSize { - id: u8, - size: u8, - }, + InvalidOptionSize { id: u8, size: u8 }, } pub type Result = std::result::Result; -#[repr(transparent)] -#[derive(Copy, Clone, Pod, Zeroable, SwapEndian, Default, Eq, PartialEq)] -pub struct TypesFlags(u16); - -impl TypesFlags { - #[inline] - pub const fn get_types(self) -> u8 { - (self.0 & 0x000F) as u8 - } - #[inline] - pub const fn get_flags(self) -> u16 { - (self.0 & 0xFFF0) >> 4 - } - #[inline] - pub const fn types(self, val: u8) -> Self { - Self((self.0 & 0xFFF0) | (val as u16 & 0x000F)) - } - #[inline] - pub const fn flags(self, val: u16) -> Self { - Self((self.0 & 0x000F) | ((val << 4) & 0xFFF0)) - } - #[inline] - pub const fn set_flag(&mut self, val: u16){ - self.0 |= (val & 0xFFF) << 4; - } - #[inline] - pub const fn set_types(&mut self, val: u8){ - self.0 |= val as u16 & 0x0F; - } -} - -pub mod flags { - pub const ACK: u16 = 0x001; - pub const RELIABLE: u16 = 0x002; - pub const NEED_ACK: u16 = 0x004; - pub const HAS_SIZE: u16 = 0x008; - pub const MULTI_ACK: u16 = 0x200; -} - -pub mod types { - pub const SYN: u8 = 0x0; - pub const CONNECT: u8 = 0x1; - pub const DATA: u8 = 0x2; - pub const DISCONNECT: u8 = 0x3; - pub const PING: u8 = 0x4; - /// no idea what user is supposed to mean - pub const USER: u8 = 0x5; -} - -impl Debug for TypesFlags { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let stream_type = self.get_types(); - let port_number = self.get_flags(); - write!(f, "TypesFlags{{ types: {}, flags: {} }}", stream_type, port_number) - } -} - -#[repr(transparent)] -#[derive(PartialEq, Eq, Ord, PartialOrd, Copy, Clone, Pod, Zeroable, SwapEndian, Hash)] -pub struct VirtualPort(pub(crate) u8); - -impl VirtualPort { - - #[inline] - pub const fn get_stream_type(self) -> u8 { - (self.0 & 0xF0) >> 4 - } - - #[inline] - pub const fn get_port_number(self) -> u8 { - self.0 & 0x0F - } - - #[inline] - pub fn stream_type(self, val: u8) -> Self { - let masked_val = val & 0x0F; - assert_eq!(masked_val, val); - - Self((self.0 & 0x0F) | (masked_val << 4)) - } - - #[inline] - pub fn port_number(self, val: u8) -> Self { - let masked_val = val & 0x0F; - assert_eq!(masked_val, val); - - Self((self.0 & 0xF0) | masked_val) - } - - #[inline] - pub fn new(port: u8, stream_type: u8) -> Self { - Self(0).stream_type(stream_type).port_number(port) - } -} - -impl Debug for VirtualPort { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let stream_type = self.get_stream_type(); - let port_number = self.get_port_number(); - write!(f, "VirtualPort{{ stream_type: {}, port_number: {} }}", stream_type, port_number) - } -} - #[repr(C)] #[derive(Debug, Copy, Clone, Pod, Zeroable, SwapEndian, Eq, PartialEq)] pub struct PRUDPV1Header { @@ -159,7 +58,7 @@ pub struct PRUDPV1Header { impl Default for PRUDPV1Header { fn default() -> Self { - Self{ + Self { magic: [0xEA, 0xD0], version: 1, session_id: 0, @@ -169,32 +68,30 @@ impl Default for PRUDPV1Header { destination_port: VirtualPort(0), types_and_flags: TypesFlags(0), packet_specific_size: 0, - substream_id: 0 + substream_id: 0, } } } - #[derive(Debug, Clone, Eq, PartialEq)] -pub enum PacketOption{ +pub enum PacketOption { SupportedFunctions(u32), ConnectionSignature([u8; 16]), FragmentId(u8), InitialSequenceId(u16), - MaximumSubstreamId(u8) + MaximumSubstreamId(u8), } -impl PacketOption{ - fn from(option_id: OptionId, option_data: &[u8]) -> io::Result{ - +impl PacketOption { + fn from(option_id: OptionId, option_data: &[u8]) -> io::Result { let mut data_cursor = Cursor::new(option_data); - let val = match option_id.into(){ + let val = match option_id.into() { 0 => SupportedFunctions(data_cursor.read_struct(IS_BIG_ENDIAN)?), 1 => ConnectionSignature(data_cursor.read_struct(IS_BIG_ENDIAN)?), 2 => FragmentId(data_cursor.read_struct(IS_BIG_ENDIAN)?), 3 => InitialSequenceId(data_cursor.read_struct(IS_BIG_ENDIAN)?), 4 => MaximumSubstreamId(data_cursor.read_struct(IS_BIG_ENDIAN)?), - _ => unreachable!() + _ => unreachable!(), }; Ok(val) @@ -255,7 +152,7 @@ impl OptionId { // Invariant is upheld because we only create the object if it doesn't violate the invariant match val { 0 | 1 | 2 | 3 | 4 => Ok(Self(val)), - _ => Err(Error::InvalidOptionId(val)) + _ => Err(Error::InvalidOptionId(val)), } } @@ -266,7 +163,7 @@ impl OptionId { 2 => 1, 3 => 2, 4 => 1, - _ => unreachable!() + _ => unreachable!(), } } } @@ -281,8 +178,7 @@ impl PRUDPV1Packet { pub fn new(reader: &mut (impl Read + Seek)) -> Result { let header: PRUDPV1Header = reader.read_struct(IS_BIG_ENDIAN)?; - if header.magic[0] != 0xEA || - header.magic[1] != 0xD0 { + if header.magic[0] != 0xEA || header.magic[1] != 0xD0 { return Err(Error::InvalidMagic(u16::from_be_bytes(header.magic))); } @@ -290,31 +186,31 @@ impl PRUDPV1Packet { return Err(Error::InvalidVersion(header.version)); } - let packet_signature: [u8; 16] = reader.read_struct(IS_BIG_ENDIAN)?; //let packet_signature: [u8; 16] = [0; 16]; assert_eq!(reader.stream_position().ok(), Some(14 + 16)); - - let mut packet_specific_buffer = vec![0u8; header.packet_specific_size as usize]; reader.read_exact(&mut packet_specific_buffer)?; - //no clue whats up with options but they are broken let mut packet_specific_data_cursor = Cursor::new(&packet_specific_buffer); let mut options = Vec::new(); loop { - let Ok(option_id): io::Result = packet_specific_data_cursor.read_struct(IS_BIG_ENDIAN) else { - break + let Ok(option_id): io::Result = + packet_specific_data_cursor.read_struct(IS_BIG_ENDIAN) + else { + break; }; - let Ok(value_size): io::Result = packet_specific_data_cursor.read_struct(IS_BIG_ENDIAN) else { - break + let Ok(value_size): io::Result = + packet_specific_data_cursor.read_struct(IS_BIG_ENDIAN) + else { + break; }; if value_size == 0 { @@ -334,20 +230,21 @@ impl PRUDPV1Packet { } let mut option_data = vec![0u8; value_size as usize]; - if packet_specific_data_cursor.read_exact(&mut option_data[..]).is_err() { + if packet_specific_data_cursor + .read_exact(&mut option_data[..]) + .is_err() + { error!("unable to read options"); break; } options.push(PacketOption::from(option_id, &option_data)?); } - + let mut payload = vec![0u8; header.payload_size as usize]; reader.read_exact(&mut payload)?; - - Ok(Self { header, packet_signature, @@ -356,22 +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 mut flags = self.header.types_and_flags.flags(0); flags.set_flag(ACK); - let options = self.options + let options = self + .options .iter() .filter(|o| matches!(o, FragmentId(_))) .cloned() .collect(); - - - Self{ + Self { header: PRUDPV1Header { types_and_flags: flags, sequence_id: self.header.sequence_id, @@ -386,22 +282,29 @@ impl PRUDPV1Packet { pub fn source_sockaddr(&self, socket_addr_v4: SocketAddrV4) -> PRUDPSockAddr { PRUDPSockAddr { - regular_socket_addr: socket_addr_v4, + regular_socket_addr: SocketAddr::V4(socket_addr_v4), virtual_port: self.header.source_port, } } - fn generate_options_bytes(&self) -> Vec{ + fn generate_options_bytes(&self) -> Vec { let mut vec = Vec::new(); - for option in &self.options{ - option.write_to_stream(&mut vec).expect("vec should always automatically be able to extend"); + for option in &self.options { + option + .write_to_stream(&mut vec) + .expect("vec should always automatically be able to extend"); } vec } - pub fn calculate_signature_value(&self, access_key: &str, session_key: Option<[u8; 32]>, connection_signature: Option<[u8; 16]>) -> [u8; 16]{ + pub fn calculate_signature_value( + &self, + access_key: &str, + session_key: Option<[u8; 32]>, + connection_signature: Option<[u8; 16]>, + ) -> [u8; 16] { let access_key_bytes = access_key.as_bytes(); let access_key_sum: u32 = access_key_bytes.iter().map(|v| *v as u32).sum(); let access_key_sum_bytes: [u8; 4] = access_key_sum.to_le_bytes(); @@ -417,32 +320,46 @@ impl PRUDPV1Packet { let mut hmac = Md5Hmac::new_from_slice(&key).expect("fuck"); - hmac.write(&header_data).expect("error during hmac calculation"); + hmac.write(&header_data) + .expect("error during hmac calculation"); if let Some(session_key) = session_key { - hmac.write(&session_key).expect("error during hmac calculation"); + hmac.write(&session_key) + .expect("error during hmac calculation"); } - hmac.write(&access_key_sum_bytes).expect("error during hmac calculation"); + hmac.write(&access_key_sum_bytes) + .expect("error during hmac calculation"); if let Some(connection_signature) = connection_signature { - hmac.write(&connection_signature).expect("error during hmac calculation"); + hmac.write(&connection_signature) + .expect("error during hmac calculation"); } - hmac.write(&option_bytes).expect("error during hmac calculation"); + hmac.write(&option_bytes) + .expect("error during hmac calculation"); - hmac.write_all(&self.payload).expect("error during hmac calculation"); + hmac.write_all(&self.payload) + .expect("error during hmac calculation"); - hmac.finalize().into_bytes()[0..16].try_into().expect("invalid hmac size") + hmac.finalize().into_bytes()[0..16] + .try_into() + .expect("invalid hmac size") } - pub fn calculate_and_assign_signature(&mut self, access_key: &str, session_key: Option<[u8; 32]>, connection_signature: Option<[u8; 16]>){ - self.packet_signature = self.calculate_signature_value(access_key, session_key, connection_signature); + pub fn calculate_and_assign_signature( + &mut self, + access_key: &str, + session_key: Option<[u8; 32]>, + connection_signature: Option<[u8; 16]>, + ) { + self.packet_signature = + self.calculate_signature_value(access_key, session_key, connection_signature); } - pub fn set_sizes(&mut self){ + pub fn set_sizes(&mut self) { self.header.packet_specific_size = self.options.iter().map(|o| o.write_size()).sum(); self.header.payload_size = self.payload.len() as u16; } - pub fn base_response_packet(&self) -> Self { + pub fn base_response_packet(&self) -> Self { Self { header: PRUDPV1Header { magic: [0xEA, 0xD0], @@ -455,19 +372,18 @@ impl PRUDPV1Packet { sequence_id: 0, session_id: 0, substream_id: 0, - }, packet_signature: [0; 16], payload: Default::default(), - options: Default::default() + options: Default::default(), } } - pub fn write_to(&self, writer: &mut impl Write) -> io::Result<()>{ + pub fn write_to(&self, writer: &mut impl Write) -> io::Result<()> { writer.write_all(bytemuck::bytes_of(&self.header))?; writer.write_all(&self.packet_signature)?; - for option in &self.options{ + for option in &self.options { option.write_to_stream(writer)?; } @@ -479,19 +395,24 @@ impl PRUDPV1Packet { #[cfg(test)] mod test { - use crate::prudp::packet::flags::{NEED_ACK, RELIABLE}; - use crate::prudp::packet::types::DATA; - use super::{OptionId, PacketOption, PRUDPV1Header, TypesFlags, VirtualPort}; + use super::{OptionId, PRUDPV1Header, PacketOption, TypesFlags}; + use rnex_core::prudp::{ + types_flags::{ + flags::{NEED_ACK, RELIABLE}, + types::DATA, + }, + virtual_port::VirtualPort, + }; #[test] fn size_test() { assert_eq!(size_of::(), 14); } #[test] - fn test_options(){ - let packet_types = [0,1,2,3,4]; + fn test_options() { + let packet_types = [0, 1, 2, 3, 4]; - for p_type in packet_types{ + for p_type in packet_types { let option_id = OptionId::new(p_type).unwrap(); let buf = vec![0; option_id.option_type_size() as usize]; @@ -505,12 +426,10 @@ mod test { assert_eq!(write_buf.len() as u8, opt.write_size()) } } - - } #[test] - fn header_read(){ + fn header_read() { let header = PRUDPV1Header { version: 0, destination_port: VirtualPort(0), @@ -520,8 +439,8 @@ mod test { packet_specific_size: 0, payload_size: 0, sequence_id: 0, - magic: [0xEA,0xD0], - source_port: VirtualPort(0) + magic: [0xEA, 0xD0], + source_port: VirtualPort(0), }; let bytes = bytemuck::bytes_of(&header); @@ -532,11 +451,11 @@ mod test { } #[test] - fn test_types_flags(){ + fn test_types_flags() { let types = TypesFlags::default().types(DATA).flags(NEED_ACK | RELIABLE); assert_ne!((types.0 >> 4) & NEED_ACK, 0); assert_ne!((types.0 >> 4) & RELIABLE, 0); assert_ne!((types.0 & 0xFF) as u8 & DATA, 0); } -} \ No newline at end of file +} diff --git a/prudpv1/src/prudp/packet/v1/sanity_checks.rs b/prudpv1/src/prudp/packet/v1/sanity_checks.rs new file mode 100644 index 0000000..335c269 --- /dev/null +++ b/prudpv1/src/prudp/packet/v1/sanity_checks.rs @@ -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"); diff --git a/src/prudp/router.rs b/prudpv1/src/prudp/router.rs similarity index 93% rename from src/prudp/router.rs rename to prudpv1/src/prudp/router.rs index 3aa8bdd..ca28dd6 100644 --- a/src/prudp/router.rs +++ b/prudpv1/src/prudp/router.rs @@ -1,33 +1,27 @@ -use std::{env, io}; +use std::io; use std::io::Cursor; use std::marker::PhantomData; use tokio::net::UdpSocket; use std::net::{SocketAddr, SocketAddrV4}; use std::net::SocketAddr::V4; use std::sync::{Arc, Weak}; -use std::sync::atomic::{AtomicBool}; use std::time::Duration; use tokio::task::JoinHandle; -use once_cell::sync::Lazy; use log::{error, info}; use thiserror::Error; use tokio::select; use tokio::sync::RwLock; use tokio::time::sleep; use crate::prudp::socket::{new_socket_pair, AnyInternalSocket, CryptoHandler, ExternalSocket}; -use crate::prudp::packet::{PRUDPV1Packet, VirtualPort}; +use crate::prudp::packet::{PRUDPV1Packet}; +use rnex_core::prudp::virtual_port::VirtualPort; use crate::prudp::router::Error::VirtualPortTaken; -static SERVER_DATAGRAMS: Lazy = Lazy::new(||{ - env::var("SERVER_DATAGRAM_COUNT").ok() - .and_then(|s| s.parse().ok()) - .unwrap_or(1) -}); pub struct Router { endpoints: RwLock<[Option>; 16]>, - running: AtomicBool, + //running: AtomicBool, socket: Arc, _no_outside_construction: PhantomData<()> } @@ -113,7 +107,7 @@ impl Router { let own_impl = Router { endpoints: Default::default(), - running: AtomicBool::new(true), + // running: AtomicBool::new(true), socket: socket.clone(), _no_outside_construction: Default::default() }; diff --git a/src/prudp/secure.rs b/prudpv1/src/prudp/secure.rs similarity index 54% rename from src/prudp/secure.rs rename to prudpv1/src/prudp/secure.rs index e0438d7..9bd441b 100644 --- a/src/prudp/secure.rs +++ b/prudpv1/src/prudp/secure.rs @@ -1,110 +1,52 @@ -use std::io::Cursor; +use crate::prudp::packet::PRUDPV1Packet; +use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance}; use hmac::digest::consts::U32; use log::error; use rc4::cipher::StreamCipherCoreWrapper; -use rc4::{KeyInit, Rc4, Rc4Core, StreamCipher}; use rc4::consts::U16; +use rc4::{KeyInit, Rc4, Rc4Core, StreamCipher}; +use rnex_core::kerberos::{TicketInternalData, derive_key}; +use rnex_core::nex::account::Account; +use rnex_core::prudp::encryption::EncryptionPair; +use rnex_core::prudp::ticket::read_secure_connection_data; +use rnex_core::rmc::structures::RmcSerialize; +use std::io::Cursor; use typenum::U5; -use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; -use crate::kerberos::{derive_key, TicketInternalData}; -use crate::nex::account::Account; -use crate::prudp::packet::PRUDPV1Packet; -use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance, EncryptionPair}; -use crate::rmc::structures::RmcSerialize; - -pub fn read_secure_connection_data(data: &[u8], act: &Account) -> Option<([u8; 32], u32, u32)>{ - let mut cursor = Cursor::new(data); - - let mut ticket_data: Vec = Vec::deserialize(&mut cursor).ok()?; - let mut request_data: Vec = 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> = - 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> = - Rc4::new_from_slice(&session_key).expect("unable to init rc4 keystream"); - - rc4.apply_keystream(request_data); - - let mut reqest_data_cursor = Cursor::new(request_data); - - let pid: u32 = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?; - - if pid != ticket_source_pid{ - let ticket_created_on = issued_time.to_regular_time(); - - error!("someone tried to spoof their pid, ticket was created on: {}", ticket_created_on.to_rfc2822()); - return None; - } - - let _cid: u32 = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?; - let response_check: u32 = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?; - - - - Some((session_key, pid, response_check)) -} +use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; type Rc4U32 = StreamCipherCoreWrapper>; -pub fn generate_secure_encryption_pairs(mut session_key: [u8; 32], count: u8) -> Vec>>{ +pub fn generate_secure_encryption_pairs( + mut session_key: [u8; 32], + count: u8, +) -> Vec>> { let mut vec = Vec::with_capacity(count as usize); - vec.push(EncryptionPair{ + vec.push(EncryptionPair { send: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"), - recv: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4") + recv: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"), }); - for _ in 1..=count{ + for _ in 1..=count { let modifier = session_key.len() + 1; let key_length = session_key.len(); - for (position, val) in (&mut session_key[0..key_length/2]).iter_mut().enumerate(){ + for (position, val) in (&mut session_key[0..key_length / 2]).iter_mut().enumerate() { *val = val.wrapping_add((modifier - position) as u8); } - vec.push(EncryptionPair{ + vec.push(EncryptionPair { send: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"), - recv: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4") + recv: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"), }); } vec } - pub struct Secure(pub &'static str, pub Account); - pub struct SecureInstance { access_key: &'static str, session_key: [u8; 32], @@ -155,18 +97,17 @@ impl CryptoHandler for Secure { } } - impl CryptoHandlerConnectionInstance for SecureInstance { type Encryption = Rc4; fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]) { - if let Some(crypt_pair) = self.streams.get_mut(substream as usize){ + if let Some(crypt_pair) = self.streams.get_mut(substream as usize) { crypt_pair.recv.apply_keystream(data); } } fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]) { - if let Some(crypt_pair) = self.streams.get_mut(substream as usize){ + if let Some(crypt_pair) = self.streams.get_mut(substream as usize) { crypt_pair.send.apply_keystream(data); } } @@ -182,10 +123,14 @@ impl CryptoHandlerConnectionInstance for SecureInstance { fn sign_packet(&self, packet: &mut PRUDPV1Packet) { packet.set_sizes(); - packet.calculate_and_assign_signature(self.access_key, Some(self.session_key), Some(self.self_signature)); + packet.calculate_and_assign_signature( + self.access_key, + Some(self.session_key), + Some(self.self_signature), + ); } fn verify_packet(&self, _packet: &PRUDPV1Packet) -> bool { true } -} \ No newline at end of file +} diff --git a/src/prudp/socket.rs b/prudpv1/src/prudp/socket.rs similarity index 76% rename from src/prudp/socket.rs rename to prudpv1/src/prudp/socket.rs index 0455bd7..a3b0445 100644 --- a/src/prudp/socket.rs +++ b/prudpv1/src/prudp/socket.rs @@ -1,43 +1,34 @@ -use crate::prudp::packet::flags::{ACK, HAS_SIZE, MULTI_ACK, NEED_ACK, RELIABLE}; -use crate::prudp::packet::types::{CONNECT, DATA, DISCONNECT, PING, SYN}; use crate::prudp::packet::PacketOption::{ ConnectionSignature, FragmentId, MaximumSubstreamId, SupportedFunctions, }; -use crate::prudp::packet::{PRUDPV1Header, PRUDPV1Packet, TypesFlags, VirtualPort}; -use crate::prudp::sockaddr::PRUDPSockAddr; +use crate::prudp::packet::{PRUDPV1Header, PRUDPV1Packet}; use async_trait::async_trait; -use log::info; use log::error; +use log::{info, warn}; use rc4::StreamCipher; +use rnex_core::prudp::socket_addr::PRUDPSockAddr; +use rnex_core::prudp::types_flags::TypesFlags; +use rnex_core::prudp::types_flags::flags::{ACK, HAS_SIZE, MULTI_ACK, NEED_ACK, RELIABLE}; +use rnex_core::prudp::types_flags::types::{CONNECT, DATA, DISCONNECT, PING, SYN}; +use rnex_core::prudp::virtual_port::VirtualPort; use std::collections::{BTreeMap, HashMap}; +use std::io::Cursor; use std::marker::PhantomData; use std::ops::Deref; use std::sync::{Arc, Weak}; +use v_byte_helpers::ReadExtensions; +use v_byte_helpers::little_endian::read_u16; use std::time::Duration; use tokio::net::UdpSocket; -use tokio::sync::mpsc::{channel, Receiver, Sender}; use tokio::sync::Mutex; -use tokio::time::{sleep, Instant}; +use tokio::sync::mpsc::{Receiver, Sender, channel}; +use tokio::time::{Instant, sleep}; // due to the way this is designed crashing the router thread causes deadlock, sorry ;-; // (maybe i will fix that some day) /// PRUDP Socket for accepting connections to then send and recieve data from those clients -pub struct EncryptionPair { - pub send: T, - pub recv: T, -} - -impl EncryptionPair { - pub fn init_both T>(func: F) -> Self { - Self { - recv: func(), - send: func(), - } - } -} - pub struct CommonConnection { pub user_id: u32, pub socket_addr: PRUDPSockAddr, @@ -50,12 +41,14 @@ struct InternalConnection { connections: Weak>>>>>, reliable_server_counter: u16, reliable_client_counter: u16, + supported_function_version: u32, // maybe add connection id(need to see if its even needed) crypto_handler_instance: E, data_sender: Sender>, socket: Arc, packet_queue: HashMap, last_packet_time: Instant, + unacknowleged_packets: Vec<(Instant, PRUDPV1Packet)>, } impl Deref for InternalConnection { @@ -66,6 +59,7 @@ impl Deref for InternalConnection { } impl InternalConnection { + /// gives back the next server packet sequence id which the client expects to send, incrementing it in the process fn next_server_count(&mut self) -> u16 { let prev_val = self.reliable_server_counter; let (val, _) = self.reliable_server_counter.overflowing_add(1); @@ -74,20 +68,30 @@ impl InternalConnection { prev_val } + /// Sends a raw packet to a given client on the connection + /// + /// a raw packet is one which does not get processed any further(other than to send it + /// off without buffering or anything), + /// as such you need to make sure that + /// the sizes are set correctly and so on #[inline] - async fn send_raw_packet(&self, mut prudp_packet: PRUDPV1Packet) { - prudp_packet.set_sizes(); + async fn send_raw_packet(&self, prudp_packet: &PRUDPV1Packet) { + send_raw_prudp_to_sockaddr(&self.socket, self.socket_addr, prudp_packet).await; + } - let mut vec = Vec::new(); + async fn delete_connection(&self) { + let Some(conns) = self.connections.upgrade() else { + // this is fine as it implies the server has already quit, thus meaning that we dont + // have to remove ourselves from the server + return; + }; - prudp_packet - .write_to(&mut vec) - .expect("somehow failed to convert backet to bytes"); + let mut conns = conns.lock().await; - self.socket - .send_to(&vec, self.socket_addr.regular_socket_addr) - .await - .expect("failed to send data back"); + conns.remove(&self.socket_addr); + + // the connection will now drop as soon as we leave this due to no longer having a permanent + // reference } } @@ -195,7 +199,9 @@ impl AnyInternalConnection for InternalConne self.crypto_handler_instance.sign_packet(&mut packet); - self.send_raw_packet(packet).await; + self.send_raw_packet(&packet).await; + + self.unacknowleged_packets.push((Instant::now(), packet)); } async fn close_connection(&mut self) { @@ -222,23 +228,29 @@ impl AnyInternalConnection for InternalConne self.crypto_handler_instance.sign_packet(&mut packet); - self.send_raw_packet(packet).await; + self.send_raw_packet(&packet).await; - let Some(conns) = self.connections.upgrade() else { - // this is fine as it implies the server has already quit, thus meaning that we dont - // have to remove ourselves from the server - return; - }; - - let mut conns = conns.lock().await; - - conns.remove(&self.socket_addr); - - // the connection will now drop as soon as we leave this due to no longer having a permanent - // reference + self.delete_connection().await; } } +async fn send_raw_prudp_to_sockaddr( + udp_socket: &UdpSocket, + dest: PRUDPSockAddr, + packet: &PRUDPV1Packet, +) { + let mut vec = Vec::new(); + + packet + .write_to(&mut vec) + .expect("somehow failed to convert backet to bytes"); + + udp_socket + .send_to(&vec, dest.regular_socket_addr) + .await + .expect("failed to send data back"); +} + impl InternalSocket { async fn get_connection( &self, @@ -256,19 +268,12 @@ impl InternalSocket { Some(conn) } - async fn send_packet_unbuffered(&self, dest: PRUDPSockAddr, mut packet: PRUDPV1Packet) { - packet.set_sizes(); - - let mut vec = Vec::new(); - - packet - .write_to(&mut vec) - .expect("somehow failed to convert backet to bytes"); - - self.socket - .send_to(&vec, dest.regular_socket_addr) - .await - .expect("failed to send data back"); + /// sends a raw packet to a specific prudp socket address + /// + /// a raw packet is a packet is a packet which wont get processed any further, + /// sizes signatures etc need to be set before using this function + async fn send_packet_unbuffered(&self, dest: PRUDPSockAddr, packet: &PRUDPV1Packet) { + send_raw_prudp_to_sockaddr(&self.socket, dest, packet).await } async fn handle_syn(&self, address: PRUDPSockAddr, packet: PRUDPV1Packet) { @@ -303,7 +308,7 @@ impl InternalSocket { //println!("got syn: {:?}", response); - self.send_packet_unbuffered(address, response).await; + self.send_packet_unbuffered(address, &response).await; } async fn connection_thread( @@ -315,7 +320,7 @@ impl InternalSocket { let mut conn = conn.lock().await; if conn.last_packet_time < (Instant::now() - Duration::from_secs(5)) { - conn.send_raw_packet(PRUDPV1Packet { + conn.send_raw_packet(&PRUDPV1Packet { header: PRUDPV1Header { sequence_id: 0, substream_id: 0, @@ -335,9 +340,24 @@ impl InternalSocket { if conn.last_packet_time < (Instant::now() - Duration::from_secs(30)) { conn.close_connection().await; } + + for (send_time, packet) in &conn.unacknowleged_packets { + if *send_time < (Instant::now() - Duration::from_millis(3000)) { + warn!( + "failed to resend packet 5 times and never got response, destroying connection" + ); + conn.close_connection().await; + break; + } + if *send_time < (Instant::now() - Duration::from_millis(500)) { + info!("unacknowledged packet sat arround for more than 500 ms, resending"); + conn.send_raw_packet(packet).await; + } + } + drop(conn); - sleep(Duration::from_secs(5)).await; + sleep(Duration::from_millis(500)).await; } } @@ -347,6 +367,7 @@ impl InternalSocket { socket_addr: PRUDPSockAddr, session_id: u8, is_instantiator: bool, + supported_function_version: u32, ) { let common = Arc::new(CommonConnection { user_id: crypto_handler_instance.get_user_id(), @@ -367,6 +388,8 @@ impl InternalSocket { socket: self.socket.clone(), packet_queue: Default::default(), last_packet_time: Instant::now(), + unacknowleged_packets: Vec::new(), + supported_function_version, }; let internal = Arc::new(Mutex::new(internal)); @@ -444,12 +467,17 @@ impl InternalSocket { .options .push(ConnectionSignature(Default::default())); + let mut functions: u32 = 0; + for option in &packet.options { match option { MaximumSubstreamId(max_substream) => { response.options.push(MaximumSubstreamId(*max_substream)) } - SupportedFunctions(funcs) => response.options.push(SupportedFunctions(*funcs & 0xFF)), + SupportedFunctions(funcs) => { + functions = *funcs & 0xFF; + response.options.push(SupportedFunctions(*funcs & 0xFF)); + } _ => { /* ? */ } } } @@ -460,10 +488,10 @@ impl InternalSocket { //println!("connect out: {:?}", response); - self.create_connection(crypto, address, session_id, false) + self.create_connection(crypto, address, session_id, false, functions) .await; - self.send_packet_unbuffered(address, response).await; + self.send_packet_unbuffered(address, &response).await; } async fn handle_data(&self, address: PRUDPSockAddr, packet: PRUDPV1Packet) { @@ -502,7 +530,7 @@ impl InternalSocket { conn.crypto_handler_instance.sign_packet(&mut response); - self.send_packet_unbuffered(address, response).await; + self.send_packet_unbuffered(address, &response).await; conn.data_sender.send(packet.payload).await.ok(); @@ -528,7 +556,7 @@ impl InternalSocket { conn.crypto_handler_instance.sign_packet(&mut response); - self.send_packet_unbuffered(address, response).await; + self.send_packet_unbuffered(address, &response).await; } async fn handle_disconnect(&self, address: PRUDPSockAddr, packet: PRUDPV1Packet) { @@ -548,10 +576,11 @@ impl InternalSocket { conn.crypto_handler_instance.sign_packet(&mut response); - self.send_packet_unbuffered(address, response.clone()).await; - self.send_packet_unbuffered(address, response.clone()).await; - self.send_packet_unbuffered(address, response).await; + self.send_packet_unbuffered(address, &response).await; + self.send_packet_unbuffered(address, &response).await; + self.send_packet_unbuffered(address, &response).await; + conn.delete_connection().await; //self.internal_connections.lock().await; } } @@ -571,7 +600,6 @@ impl AnyInternalSocket for InternalSocket { if (packet.header.types_and_flags.get_flags() & ACK) != 0 { info!("got ack"); - if packet.header.types_and_flags.get_types() == SYN || packet.header.types_and_flags.get_types() == CONNECT { @@ -596,13 +624,76 @@ impl AnyInternalSocket for InternalSocket { } else { error!("got connection response without the active reciever being present"); } + return; + } + + info!("got ack"); + + if let Some(conn) = self.get_connection(address).await { + let mut conn = conn.lock().await; + + // remove the packet whose sequence id matches the ack packet + // or in other words keep all of those which dont match the sequence id + conn.unacknowleged_packets + .retain_mut(|v| packet.header.sequence_id != v.1.header.sequence_id); + } else { + error!("non connection acknowledgement packet on nonexistent connection...") } return; } if (packet.header.types_and_flags.get_flags() & MULTI_ACK) != 0 { - info!("got multi ack"); + if let Some(conn) = self.get_connection(address).await { + let mut conn = conn.lock().await; + + if conn.supported_function_version == 1 { + let mut collected_ids: Vec = 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 = Vec::new(); + let mut cursor = Cursor::new(&packet.payload); + + let Ok(_substream_id): Result = cursor.read_le_struct() else { + error!("invalid data whilest reading new version agregate acknowledgement"); + return; + }; + let Ok(additional_sequence_ids): Result = cursor.read_le_struct() else { + error!("invalid data whilest reading new version agregate acknowledgement"); + return; + }; + let Ok(sequence_id): Result = 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 = cursor.read_le_struct() + else { + error!( + "invalid data whilest reading new version agregate acknowledgement" + ); + return; + }; + collected_ids.push(additional_sequence_id); + } + + conn.unacknowleged_packets.retain_mut(|(_, up)| { + !(collected_ids.iter().any(|id| up.header.sequence_id == *id) + || up.header.sequence_id <= sequence_id) + }); + } + } else { + error!("non connection acknowledgement packet on nonexistent connection...") + } return; } @@ -630,7 +721,7 @@ impl AnyInternalSocket for InternalSocket { let remote_signature = address.calculate_connection_signature(); - let packet = PRUDPV1Packet { + let mut packet = PRUDPV1Packet { header: PRUDPV1Header { source_port: self.virtual_port, destination_port: address.virtual_port, @@ -645,7 +736,9 @@ impl AnyInternalSocket for InternalSocket { ..Default::default() }; - self.send_packet_unbuffered(address, packet).await; + packet.set_sizes(); + + self.send_packet_unbuffered(address, &packet).await; let Some(syn_ack_packet) = recv.recv().await else { error!("what"); @@ -661,7 +754,7 @@ impl AnyInternalSocket for InternalSocket { return None; }; - let packet = PRUDPV1Packet { + let mut packet = PRUDPV1Packet { header: PRUDPV1Header { source_port: self.virtual_port, destination_port: address.virtual_port, @@ -676,9 +769,11 @@ impl AnyInternalSocket for InternalSocket { ..Default::default() }; - self.send_packet_unbuffered(address, packet).await; + packet.set_sizes(); - let Some(connect_ack_packet) = recv.recv().await else { + self.send_packet_unbuffered(address, &packet).await; + + let Some(_connect_ack_packet) = recv.recv().await else { error!("what"); return None; }; @@ -688,7 +783,7 @@ impl AnyInternalSocket for InternalSocket { .instantiate(remote_signature, *own_signature, &[], 1)?; //todo: make this work for secure servers as well - self.create_connection(crypt, address, 0, true).await; + self.create_connection(crypt, address, 0, true, 4).await; Some(()) } diff --git a/src/prudp/unsecure.rs b/prudpv1/src/prudp/unsecure.rs similarity index 92% rename from src/prudp/unsecure.rs rename to prudpv1/src/prudp/unsecure.rs index 6d7d9ff..d47550f 100644 --- a/src/prudp/unsecure.rs +++ b/prudpv1/src/prudp/unsecure.rs @@ -1,13 +1,12 @@ +use crate::prudp::packet::PRUDPV1Packet; +use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance}; use once_cell::sync::Lazy; use rc4::{Key, KeyInit, Rc4, StreamCipher}; +use rnex_core::prudp::encryption::{DEFAULT_KEY, EncryptionPair}; use typenum::U5; -use crate::prudp::packet::PRUDPV1Packet; -use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance, EncryptionPair}; pub struct Unsecure(pub &'static str); - - pub struct UnsecureInstance { key: &'static str, streams: Vec>>, @@ -18,7 +17,6 @@ pub struct UnsecureInstance { // my hand was forced to use lazy so that we can guarantee this code // only runs once and so that i can put it here as a "constant" (for performance and readability) // since for some reason rust crypto doesn't have any const time key initialization -static DEFAULT_KEY: Lazy> = Lazy::new(|| Key::from(*b"CD&ML")); impl CryptoHandler for Unsecure { type CryptoConnectionInstance = UnsecureInstance; @@ -53,13 +51,13 @@ impl CryptoHandlerConnectionInstance for UnsecureInstance { type Encryption = Rc4; fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]) { - if let Some(crypt_pair) = self.streams.get_mut(substream as usize){ + if let Some(crypt_pair) = self.streams.get_mut(substream as usize) { crypt_pair.recv.apply_keystream(data); } } fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]) { - if let Some(crypt_pair) = self.streams.get_mut(substream as usize){ + if let Some(crypt_pair) = self.streams.get_mut(substream as usize) { crypt_pair.send.apply_keystream(data); } } @@ -78,7 +76,7 @@ impl CryptoHandlerConnectionInstance for UnsecureInstance { packet.calculate_and_assign_signature(self.key, None, Some(self.self_signature)); } - fn verify_packet(&self, packet: &PRUDPV1Packet) -> bool { + fn verify_packet(&self, _packet: &PRUDPV1Packet) -> bool { true } -} \ No newline at end of file +} diff --git a/rnex-core/Cargo.toml b/rnex-core/Cargo.toml new file mode 100644 index 0000000..ff2b7b3 --- /dev/null +++ b/rnex-core/Cargo.toml @@ -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" diff --git a/rnex-core/benches/rmc_serialization.rs b/rnex-core/benches/rmc_serialization.rs new file mode 100644 index 0000000..4b506f8 --- /dev/null +++ b/rnex-core/benches/rmc_serialization.rs @@ -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 = 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> = Lazy::new(|| serialize_to_vec(DUMMY.deref())); + +fn serialize_to_vec(r: &impl RmcSerialize) -> Vec{ + let mut vec = r.to_data(); + + vec.unwrap() +} + +fn read_struct(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::(black_box(ser)))); +} + +criterion_group!(benches, matchmake_with_param); +criterion_main!(benches); + diff --git a/src/common.rs b/rnex-core/src/common.rs similarity index 100% rename from src/common.rs rename to rnex-core/src/common.rs diff --git a/rnex-core/src/executables/backend_server_insecure.rs b/rnex-core/src/executables/backend_server_insecure.rs new file mode 100644 index 0000000..b7ce979 --- /dev/null +++ b/rnex-core/src/executables/backend_server_insecure.rs @@ -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 = 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::::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; +} diff --git a/rnex-core/src/executables/backend_server_secure.rs b/rnex-core/src/executables/backend_server_secure.rs new file mode 100644 index 0000000..91b83b3 --- /dev/null +++ b/rnex-core/src/executables/backend_server_secure.rs @@ -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 + } + } +} diff --git a/rnex-core/src/executables/common.rs b/rnex-core/src/executables/common.rs new file mode 100644 index 0000000..416e450 --- /dev/null +++ b/rnex-core/src/executables/common.rs @@ -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> { + let mut req = ureq::get(IP_REQ_SERVICE_URL).call()?; + + Ok(req.body_mut().read_to_string()?.parse()?) +} + +pub static OWN_IP_PRIVATE: Lazy = 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 = 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 = Lazy::new(|| { + env::var("SERVER_PORT") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(10000) +}); + +pub static KERBEROS_SERVER_PASSWORD: Lazy = Lazy::new(|| { + env::var("AUTH_SERVER_PASSWORD") + .ok() + .unwrap_or("password".to_owned()) +}); + +pub static AUTH_SERVER_ACCOUNT: Lazy = + Lazy::new(|| Account::new(1, "Quazal Authentication", &KERBEROS_SERVER_PASSWORD)); +pub static SECURE_SERVER_ACCOUNT: Lazy = + Lazy::new(|| Account::new(2, "Quazal Rendez-Vous", &KERBEROS_SERVER_PASSWORD)); + +pub async fn new_simple_backend(mut creation_function: F) +where + F: FnMut(ConnectionInitData, RmcConnection) -> Arc, +{ + 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)); + } +} diff --git a/src/executables/edge_node_holder_server.rs b/rnex-core/src/executables/edge_node_holder_server.rs similarity index 82% rename from src/executables/edge_node_holder_server.rs rename to rnex-core/src/executables/edge_node_holder_server.rs index 021dbb1..ea798c5 100644 --- a/src/executables/edge_node_holder_server.rs +++ b/rnex-core/src/executables/edge_node_holder_server.rs @@ -1,17 +1,16 @@ use std::io::Cursor; use std::net::SocketAddrV4; use std::sync::{Arc, Weak}; -use log::error; use macros::rmc_struct; use tokio::net::TcpListener; use tokio::sync::RwLock; -use rust_nex::common::setup; -use rust_nex::executables::common::{OWN_IP_PRIVATE, SERVER_PORT}; -use rust_nex::reggie::{EdgeNodeHolderConnectOption, EdgeNodeManagement, LocalEdgeNodeHolder}; -use rust_nex::rmc::protocols::new_rmc_gateway_connection; -use rust_nex::rmc::response::ErrorCode; -use rust_nex::util::SplittableBufferConnection; -use rust_nex::rmc::structures::RmcSerialize; +use rnex_core::common::setup; +use rnex_core::executables::common::{OWN_IP_PRIVATE, SERVER_PORT}; +use rnex_core::reggie::{EdgeNodeHolderConnectOption, EdgeNodeManagement, LocalEdgeNodeHolder}; +use rnex_core::rmc::protocols::new_rmc_gateway_connection; +use rnex_core::rmc::response::ErrorCode; +use rnex_core::util::SplittableBufferConnection; +use rnex_core::rmc::structures::RmcSerialize; #[rmc_struct(EdgeNodeHolder)] struct EdgeNode{ @@ -56,7 +55,7 @@ async fn main() { let holder: Arc = Default::default(); - while let Ok((mut stream, addr)) = listen.accept().await { + while let Ok((stream, _addr)) = listen.accept().await { let mut conn: SplittableBufferConnection = stream.into(); let Some(data) = conn.recv().await else { diff --git a/rnex-core/src/executables/friends_backend.rs b/rnex-core/src/executables/friends_backend.rs new file mode 100644 index 0000000..5dfcf02 --- /dev/null +++ b/rnex-core/src/executables/friends_backend.rs @@ -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, + }) + }); + } + } +} diff --git a/rnex-core/src/executables/mod.rs b/rnex-core/src/executables/mod.rs new file mode 100644 index 0000000..cc9b83d --- /dev/null +++ b/rnex-core/src/executables/mod.rs @@ -0,0 +1,3 @@ +pub mod common; +pub mod friends_backend; +pub mod regular_backend; diff --git a/rnex-core/src/executables/regular_backend.rs b/rnex-core/src/executables/regular_backend.rs new file mode 100644 index 0000000..65cb2ca --- /dev/null +++ b/rnex-core/src/executables/regular_backend.rs @@ -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; +} diff --git a/src/grpc/account.rs b/rnex-core/src/grpc/account.rs similarity index 53% rename from src/grpc/account.rs rename to rnex-core/src/grpc/account.rs index d369501..d6ad140 100644 --- a/src/grpc/account.rs +++ b/rnex-core/src/grpc/account.rs @@ -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; -static API_KEY: Lazy = Lazy::new(||{ - let key = env::var("ACCOUNT_GQL_API_KEY") - .expect("no graphql ip specified"); +use json::{JsonValue, object}; +use once_cell::sync::Lazy; +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 = Lazy::new(|| { + let key = env::var("ACCOUNT_GQL_API_KEY").expect("no graphql ip specified"); key }); -static CLIENT_URI: Lazy = Lazy::new(||{ +static CLIENT_URI: Lazy = Lazy::new(|| { env::var("ACCOUNT_GQL_URL") .ok() .and_then(|s| s.parse().ok()) .expect("no graphql ip specified") }); - - #[derive(Error, Debug)] -pub enum Error{ +pub enum Error { #[error(transparent)] - Creation(#[from] reqwest::Error), + RequestError(#[from] ureq::Error), #[error(transparent)] Json(#[from] json::Error), - #[error(transparent)] - Status(#[from] tonic::Status), + //#[error(transparent)] + //Status(#[from] tonic::Status), #[error("invalid password size: {0}")] PasswordConversion(#[from] TryFromSliceError), #[error("something happened")] - SomethingHappened + SomethingHappened, + #[error("error joining blocking task: {0}")] + Join(#[from] JoinError), } pub type Result = result::Result; -pub struct Client(reqwest::Client); +pub struct Client; //(reqwest::Client); -impl Client{ +impl Client { pub async fn new() -> Result { - Ok(Self(reqwest::ClientBuilder::new().build()?)) + //Ok(Self(reqwest::ClientBuilder::new().build()?)) + Ok(Self) } - async fn do_request(&self, request_data: JsonValue) -> Result{ + async fn do_request(&self, request_data: JsonValue) -> Result { + let request = ureq::post(CLIENT_URI.as_str()) + .header("X-API-Key", API_KEY.deref()) + .content_type("application/json"); + let mut response = spawn_blocking(move || request.send(request_data.to_string())).await??; + + let str_body = response.body_mut().read_to_string()?; + Ok(json::parse(&str_body)?) + /* let mut request = reqwest::Request::new(Method::POST, Url::from_str(CLIENT_URI.as_str()).unwrap()); *(request.body_mut()) = Some(Body::from(request_data.to_string())); @@ -56,36 +64,81 @@ impl Client{ let response = self.0.execute(request).await?; Ok(json::parse(&response.text().await?)?) + + */ } - pub async fn get_nex_password(&mut self , pid: u32) -> Result<[u8; 16]>{ - let req = self.do_request(object!{ - "query": r"query($pid: Int!){ + pub async fn get_nex_password(&mut self, pid: PID) -> Result<[u8; 16]> { + let req = self + .do_request(object! { + "query": r"query($pid: Int!){ userByPid(pid: $pid){ nexPassword } }", - "variables": { - "pid": pid - } - }).await?; + "variables": { + "pid": pid + } + }) + .await?; - let Some(val) = req.entries() + let Some(val) = req + .entries() .find(|v| v.0 == "data") - .ok_or(SomethingHappened)?.1 + .ok_or(SomethingHappened)? + .1 .entries() .find(|v| v.0 == "userByPid") - .ok_or(SomethingHappened)?.1 + .ok_or(SomethingHappened)? + .1 .entries() .find(|v| v.0 == "nexPassword") - .ok_or(SomethingHappened)?.1 - .as_str() else { + .ok_or(SomethingHappened)? + .1 + .as_str() + else { return Err(SomethingHappened); }; Ok(val.as_bytes().try_into().map_err(|_| SomethingHappened)?) } + pub async fn get_pid_from_token(&mut self, token: String) -> Result { + 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{ let req = Request::new(GetUserDataRequest{ pid @@ -97,8 +150,6 @@ impl Client{ }*/ } - - /* pub struct Client(AccountClient>); @@ -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); - } - - -} \ No newline at end of file diff --git a/src/grpc/mod.rs b/rnex-core/src/grpc/mod.rs similarity index 62% rename from src/grpc/mod.rs rename to rnex-core/src/grpc/mod.rs index 2ca742e..c96ccfe 100644 --- a/src/grpc/mod.rs +++ b/rnex-core/src/grpc/mod.rs @@ -2,8 +2,7 @@ //! before account rs is finished. //! //! This WILL be deprecated as soon as account rs is in a stable state. -use tonic::{Request, Status}; +//use tonic::{Request, Status}; -type InterceptorFunc = Box<(dyn Fn(Request<()>) -> Result, Status> + Send)>; -mod protobufs; +//type InterceptorFunc = Box) -> Result, Status> + Send>; pub mod account; \ No newline at end of file diff --git a/rnex-core/src/kerberos/mod.rs b/rnex-core/src/kerberos/mod.rs new file mode 100644 index 0000000..205c62a --- /dev/null +++ b/rnex-core/src/kerberos/mod.rs @@ -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; + +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 { + 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 { + 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> = Rc4::new_from_slice(&key).unwrap(); + rc4.apply_keystream(&mut data); + + let mut hmac = ::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> = Rc4::new_from_slice(&key).unwrap(); + rc4.apply_keystream(&mut data); + + let mut hmac = ::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()); + } +} diff --git a/src/lib.rs b/rnex-core/src/lib.rs similarity index 64% rename from src/lib.rs rename to rnex-core/src/lib.rs index 53553c2..28bdd48 100644 --- a/src/lib.rs +++ b/rnex-core/src/lib.rs @@ -4,23 +4,29 @@ #![allow(async_fn_in_trait)] //#![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 rmc; //mod protocols; +pub mod common; +pub mod executables; pub mod grpc; pub mod kerberos; pub mod nex; -pub mod result; -pub mod versions; -pub mod web; -pub mod common; pub mod reggie; +pub mod result; pub mod rnex_proxy_common; 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"); +} diff --git a/rnex-core/src/main.rs b/rnex-core/src/main.rs new file mode 100644 index 0000000..eda5915 --- /dev/null +++ b/rnex-core/src/main.rs @@ -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 = 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> = Lazy::new(|| serialize_to_vec(DUMMY.deref())); + +fn serialize_to_vec(r: &impl RmcSerialize) -> Vec{ + let vec = r.to_data(); + + vec.unwrap() +} + +fn read_struct(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::(black_box(DUMMY_SER.deref().as_slice())); + black_box(v); + black_box(u); + } +} \ No newline at end of file diff --git a/rnex-core/src/nex/account.rs b/rnex-core/src/nex/account.rs new file mode 100644 index 0000000..eb542ff --- /dev/null +++ b/rnex-core/src/nex/account.rs @@ -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) + } +} diff --git a/rnex-core/src/nex/auth_handler.rs b/rnex-core/src/nex/auth_handler.rs new file mode 100644 index 0000000..341dc93 --- /dev/null +++ b/rnex-core/src/nex/auth_handler.rs @@ -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>, +} + +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 = + 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, 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, 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, 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, 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), 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 { + Err(ErrorCode::Core_Exception) + } + + async fn get_name(&self, _pid: PID) -> Result { + 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, 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::()]); + + let remainder = &data.2[size_of::()..]; + + println!("tick: {:?}", tick); + let data = as RmcSerialize>::deserialize(&mut Cursor::new(remainder)).unwrap(); + println!("inner ticket raw: {:?}", data); + + println!("{:?}", data); + } +} diff --git a/rnex-core/src/nex/common.rs b/rnex-core/src/nex/common.rs new file mode 100644 index 0000000..48b080d --- /dev/null +++ b/rnex-core/src/nex/common.rs @@ -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, ErrorCode> { + let mut public_station: Option = None; + let mut private_station: Option = 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]) +} diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs new file mode 100644 index 0000000..941b747 --- /dev/null +++ b/rnex-core/src/nex/friends_handler.rs @@ -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, + pub addr: PRUDPSockAddr, + pub pid: PID, + pub data: RwLock>, + pub current_friends: RwLock>, + pub this: Weak, + pub remote: RemoteFriendRemote, +} + +#[rmc_struct(FriendsGuest)] +pub struct FriendsGuest { + pub fm: Arc, + pub addr: PRUDPSockAddr, +} + +pub struct FriendsManager { + pub cid_counter: AtomicU32, + pub users: RwLock>>, +} + +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, + Vec, + Vec, + Vec, + bool, + Vec, + 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, + ) -> Result<(), ErrorCode> { + Ok(()) + } + + async fn check_setting_status(&self) -> Result { + Ok(0xFF) + } +} + +type HMacMd5 = hmac::Hmac; + +impl Secure for FriendsUser { + async fn register( + &self, + station_urls: Vec, + ) -> 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, + _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, + ) -> 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, + _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) + } +} diff --git a/rnex-core/src/nex/matchmake.rs b/rnex-core/src/nex/matchmake.rs new file mode 100644 index 0000000..0a18f74 --- /dev/null +++ b/rnex-core/src/nex/matchmake.rs @@ -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>>>, + pub rv_cid_counter: AtomicU32, + pub users: RwLock>>, +} + +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>, 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) { + 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>, +} + +fn read_bounds_string(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(compare: T, str: &str) -> Option { + let bounds: (T, T) = read_bounds_string(str)?; + + Some(bounds.0 <= compare && compare <= bounds.1) +} + +pub async fn broadcast_notification>( + 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> { + 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, + ) -> 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], 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 { + // 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(()) + } +} diff --git a/src/nex/mod.rs b/rnex-core/src/nex/mod.rs similarity index 56% rename from src/nex/mod.rs rename to rnex-core/src/nex/mod.rs index 76791bc..0f52c13 100644 --- a/src/nex/mod.rs +++ b/rnex-core/src/nex/mod.rs @@ -1,5 +1,7 @@ pub mod account; pub mod auth_handler; -pub mod user; +pub mod common; +pub mod friends_handler; +pub mod matchmake; pub mod remote_console; -pub mod matchmake; \ No newline at end of file +pub mod user; diff --git a/src/nex/remote_console.rs b/rnex-core/src/nex/remote_console.rs similarity index 100% rename from src/nex/remote_console.rs rename to rnex-core/src/nex/remote_console.rs diff --git a/rnex-core/src/nex/user.rs b/rnex-core/src/nex/user.rs new file mode 100644 index 0000000..57c36d8 --- /dev/null +++ b/rnex-core/src/nex/user.rs @@ -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, + pub remote: RemoteConsole, + pub station_url: RwLock>, + pub matchmake_manager: Arc, +} + +impl Secure for User { + async fn register( + &self, + station_urls: Vec, + ) -> 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, + _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) -> Result, 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 { + 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 { + 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 { + 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 { + 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 { + Ok(true) + } + async fn get_session_urls(&self, gid: u32) -> Result, 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, + _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 { + 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, + 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, 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, _> = 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, 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 = 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 = 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 { + 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) + } +} diff --git a/rnex-core/src/prudp/encryption.rs b/rnex-core/src/prudp/encryption.rs new file mode 100644 index 0000000..aef3739 --- /dev/null +++ b/rnex-core/src/prudp/encryption.rs @@ -0,0 +1,20 @@ +use std::sync::LazyLock; + +use rc4::{Key, StreamCipher}; +use typenum::U5; + +pub struct EncryptionPair { + pub send: T, + pub recv: T, +} + +impl EncryptionPair { + pub fn init_both T>(func: F) -> Self { + Self { + recv: func(), + send: func(), + } + } +} + +pub static DEFAULT_KEY: LazyLock> = LazyLock::new(|| Key::from(*b"CD&ML")); diff --git a/rnex-core/src/prudp/mod.rs b/rnex-core/src/prudp/mod.rs new file mode 100644 index 0000000..501463a --- /dev/null +++ b/rnex-core/src/prudp/mod.rs @@ -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; diff --git a/rnex-core/src/prudp/socket_addr.rs b/rnex-core/src/prudp/socket_addr.rs new file mode 100644 index 0000000..e7cdf94 --- /dev/null +++ b/rnex-core/src/prudp/socket_addr.rs @@ -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; + +#[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 + } +} diff --git a/rnex-core/src/prudp/station_url.rs b/rnex-core/src/prudp/station_url.rs new file mode 100644 index 0000000..e2d4abf --- /dev/null +++ b/rnex-core/src/prudp/station_url.rs @@ -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, +} + +impl StationUrl { + pub fn read_options(options: &str) -> Option> { + 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 { + 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 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 { + 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 { + 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) + } +} diff --git a/rnex-core/src/prudp/ticket.rs b/rnex-core/src/prudp/ticket.rs new file mode 100644 index 0000000..98e181f --- /dev/null +++ b/rnex-core/src/prudp/ticket.rs @@ -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 = Vec::deserialize(&mut cursor).ok()?; + let mut request_data: Vec = 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> = + 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> = + 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)) +} diff --git a/rnex-core/src/prudp/types_flags.rs b/rnex-core/src/prudp/types_flags.rs new file mode 100644 index 0000000..e9886e4 --- /dev/null +++ b/rnex-core/src/prudp/types_flags.rs @@ -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; +} diff --git a/rnex-core/src/prudp/virtual_port.rs b/rnex-core/src/prudp/virtual_port.rs new file mode 100644 index 0000000..625709b --- /dev/null +++ b/rnex-core/src/prudp/virtual_port.rs @@ -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 { + 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 + ) + } +} diff --git a/rnex-core/src/reggie.rs b/rnex-core/src/reggie.rs new file mode 100644 index 0000000..39f12de --- /dev/null +++ b/rnex-core/src/reggie.rs @@ -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, 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 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 UnitPacketWrite for T {} + +#[rmc_proto(1)] +pub trait EdgeNodeManagement { + #[method_id(1)] + async fn get_url(&self, seed: u64) -> Result; +} + +define_rmc_proto!( + proto EdgeNodeHolder{ + EdgeNodeManagement + } +); + +#[derive(RmcSerialize, Debug)] +#[repr(u32)] +pub enum EdgeNodeHolderConnectOption { + DontRegister = 0, + Register(SocketAddrV4) = 1, +} diff --git a/src/result.rs b/rnex-core/src/result.rs similarity index 100% rename from src/result.rs rename to rnex-core/src/result.rs diff --git a/src/rmc/message.rs b/rnex-core/src/rmc/message.rs similarity index 92% rename from src/rmc/message.rs rename to rnex-core/src/rmc/message.rs index f45b585..4fcdfbe 100644 --- a/src/rmc/message.rs +++ b/rnex-core/src/rmc/message.rs @@ -2,7 +2,7 @@ use std::io; use std::io::{Read, Seek, Write}; use bytemuck::bytes_of; use log::error; -use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; +use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; use crate::rmc::response::{ErrorCode, RMCResponseResult}; #[derive(Debug, Clone, PartialEq, Eq)] @@ -42,7 +42,8 @@ impl RMCMessage{ 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. Ok(Self{ diff --git a/src/rmc/mod.rs b/rnex-core/src/rmc/mod.rs similarity index 100% rename from src/rmc/mod.rs rename to rnex-core/src/rmc/mod.rs diff --git a/rnex-core/src/rmc/protocols/account_management.rs b/rnex-core/src/rmc/protocols/account_management.rs new file mode 100644 index 0000000..8ef2d9e --- /dev/null +++ b/rnex-core/src/rmc/protocols/account_management.rs @@ -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>; +} diff --git a/src/rmc/protocols/auth.rs b/rnex-core/src/rmc/protocols/auth.rs similarity index 60% rename from src/rmc/protocols/auth.rs rename to rnex-core/src/rmc/protocols/auth.rs index ed8d1cd..ca387f9 100644 --- a/src/rmc/protocols/auth.rs +++ b/rnex-core/src/rmc/protocols/auth.rs @@ -1,36 +1,47 @@ -use crate::rmc::response::ErrorCode; -use crate::rmc::structures::any::Any; -use crate::rmc::structures::connection_data::ConnectionData; -use crate::rmc::structures::qresult::QResult; +use crate::rmc::structures::connection_data::{ConnectionData, ConnectionDataOld}; +use cfg_if::cfg_if; 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, ConnectionData, String, String); + type REQUEST_TICKET_RET = (QResult, Vec, String); + } else { + type LOGIN_EX_RET = (QResult, PID, Vec, ConnectionData, String); + type REQUEST_TICKET_RET = (QResult, Vec); + } +} -/// This is the representation for `Ticket Granting`(for details see the +/// This is the representation for `Ticket Granting`(for details see the /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) #[rmc_proto(10)] pub trait Auth { /// representation of the `Login` method(for details see the /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) #[method_id(1)] - async fn login(&self, name: String) -> Result<(), ErrorCode>; + async fn login( + &self, + name: String, + ) -> Result<(QResult, PID, Vec, ConnectionDataOld, String), ErrorCode>; /// representation of the `LoginEx` method(for details see the /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) #[method_id(2)] - async fn login_ex( - &self, - name: String, - extra_data: Any, - ) -> Result<(QResult, u32, Vec, ConnectionData, String), ErrorCode>; + async fn login_ex(&self, name: String, extra_data: Any) -> Result; - /// representation of the `RequestTicket` method(for details see the - /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) #[method_id(3)] async fn request_ticket( &self, - source_pid: u32, - destination_pid: u32, - ) -> Result<(QResult, Vec), ErrorCode>; + source_pid: PID, + destination_pid: PID, + ) -> Result; + + /// 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 /// [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 /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) #[method_id(5)] - async fn get_name(&self, pid: u32) -> Result; + async fn get_name(&self, pid: PID) -> Result; // `LoginWithContext` is left out here because we don't need it right now and versioning still // needs to be figured out diff --git a/rnex-core/src/rmc/protocols/friends.rs b/rnex-core/src/rmc/protocols/friends.rs new file mode 100644 index 0000000..81dd63e --- /dev/null +++ b/rnex-core/src/rmc/protocols/friends.rs @@ -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, + 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, + 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, + Vec, + Vec, + Vec, + bool, + Vec, + 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, + ) -> Result<(), ErrorCode>; + #[method_id(19)] + async fn check_setting_status(&self) -> Result; +} diff --git a/src/rmc/protocols/matchmake.rs b/rnex-core/src/rmc/protocols/matchmake.rs similarity index 56% rename from src/rmc/protocols/matchmake.rs rename to rnex-core/src/rmc/protocols/matchmake.rs index 1c2d5e5..94edce3 100644 --- a/src/rmc/protocols/matchmake.rs +++ b/rnex-core/src/rmc/protocols/matchmake.rs @@ -1,17 +1,22 @@ use macros::{method_id, rmc_proto}; -use crate::prudp::station_url::StationUrl; -use crate::rmc::response::ErrorCode; +use rnex_core::prudp::station_url::StationUrl; +use rnex_core::rmc::response::ErrorCode; + +use rnex_core::PID; #[rmc_proto(21)] -pub trait Matchmake{ +pub trait Matchmake { #[method_id(2)] async fn unregister_gathering(&self, gid: u32) -> Result; #[method_id(41)] async fn get_session_urls(&self, gid: u32) -> Result, ErrorCode>; - #[method_id(42)] async fn update_session_host(&self, gid: u32, change_owner: bool) -> Result<(), ErrorCode>; - #[method_id(44)] - async fn migrate_gathering_ownership(&self, gid: u32, candidates: Vec, participants_only: bool) -> Result<(), ErrorCode>; -} \ No newline at end of file + async fn migrate_gathering_ownership( + &self, + gid: u32, + candidates: Vec, + participants_only: bool, + ) -> Result<(), ErrorCode>; +} diff --git a/src/rmc/protocols/matchmake_ext.rs b/rnex-core/src/rmc/protocols/matchmake_ext.rs similarity index 82% rename from src/rmc/protocols/matchmake_ext.rs rename to rnex-core/src/rmc/protocols/matchmake_ext.rs index b795d3d..46c1c09 100644 --- a/src/rmc/protocols/matchmake_ext.rs +++ b/rnex-core/src/rmc/protocols/matchmake_ext.rs @@ -1,5 +1,5 @@ use macros::{method_id, rmc_proto}; -use crate::rmc::response::ErrorCode; +use rnex_core::rmc::response::ErrorCode; #[rmc_proto(50)] pub trait MatchmakeExt{ diff --git a/src/rmc/protocols/matchmake_extension.rs b/rnex-core/src/rmc/protocols/matchmake_extension.rs similarity index 87% rename from src/rmc/protocols/matchmake_extension.rs rename to rnex-core/src/rmc/protocols/matchmake_extension.rs index cd3f281..fff5252 100644 --- a/src/rmc/protocols/matchmake_extension.rs +++ b/rnex-core/src/rmc/protocols/matchmake_extension.rs @@ -1,6 +1,6 @@ use macros::{method_id, rmc_proto}; -use crate::rmc::response::ErrorCode; -use crate::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession}; +use rnex_core::rmc::response::ErrorCode; +use rnex_core::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession}; #[rmc_proto(109)] pub trait MatchmakeExtension{ diff --git a/src/rmc/protocols/mod.rs b/rnex-core/src/rmc/protocols/mod.rs similarity index 72% rename from src/rmc/protocols/mod.rs rename to rnex-core/src/rmc/protocols/mod.rs index b206b44..eed0597 100644 --- a/src/rmc/protocols/mod.rs +++ b/rnex-core/src/rmc/protocols/mod.rs @@ -1,20 +1,24 @@ #![allow(async_fn_in_trait)] +pub mod account_management; pub mod auth; -pub mod secure; -pub mod notifications; +pub mod friends; pub mod matchmake; +pub mod matchmake_ext; pub mod matchmake_extension; pub mod nat_traversal; -pub mod matchmake_ext; +pub mod nintendo_notification; +pub mod notifications; pub mod ranking; +pub mod secure; -use crate::util::{SendingBufferConnection, SplittableBufferConnection}; +use crate::result::ResultExtension; use crate::rmc::message::RMCMessage; use crate::rmc::protocols::RemoteCallError::ConnectionBroke; use crate::rmc::response::{ErrorCode, RMCResponse, RMCResponseResult}; use crate::rmc::structures; use crate::rmc::structures::RmcSerialize; +use crate::util::{SendingBufferConnection, SplittableBufferConnection}; use log::{error, info}; use std::collections::HashMap; use std::future::Future; @@ -24,8 +28,7 @@ use std::sync::Arc; use std::time::Duration; use thiserror::Error; use tokio::sync::{Mutex, Notify}; -use tokio::time::{sleep, sleep_until, Instant}; -use crate::result::ResultExtension; +use tokio::time::{Instant, sleep, sleep_until}; #[derive(Error, Debug)] pub enum RemoteCallError { @@ -68,7 +71,7 @@ impl RmcConnection { Ok(()) } - pub async fn disconnect(&self){ + pub async fn disconnect(&self) { self.0.disconnect().await; } } @@ -92,15 +95,11 @@ impl RmcResponseReceiver { let mut locked = self.1.lock().await; if let Some(v) = locked.remove(&call_id) { - match v.response_result{ - RMCResponseResult::Success { - data, - .. - } => return Ok(data), - RMCResponseResult::Error { - error_code, - .. - } => return Err(RemoteCallError::ServerError(error_code)) + match v.response_result { + RMCResponseResult::Success { data, .. } => return Ok(data), + RMCResponseResult::Error { error_code, .. } => { + return Err(RemoteCallError::ServerError(error_code)); + } } } @@ -153,8 +152,9 @@ macro_rules! define_rmc_proto { $($protocol:path),* }) => { paste::paste!{ + #[allow(unused_variables)] pub trait []: std::any::Any $( + [] + $protocol)* { - async fn rmc_call(&self, remote_response_connection: &rust_nex::util::SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec){ + async fn rmc_call(&self, remote_response_connection: &rnex_core::util::SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec){ match protocol_id{ $( []::PROTOCOL_ID => ]>::rmc_call_proto(self, remote_response_connection, method_id, call_id, rest).await, @@ -164,19 +164,23 @@ macro_rules! define_rmc_proto { } } - pub struct [](rust_nex::rmc::protocols::RmcConnection); + pub struct [](rnex_core::rmc::protocols::RmcConnection); - impl rust_nex::rmc::protocols::RemoteInstantiatable for []{ - fn new(conn: rust_nex::rmc::protocols::RmcConnection) -> Self{ + impl rnex_core::rmc::protocols::RmcPureRemoteObject for []{ + fn new(conn: rnex_core::rmc::protocols::RmcConnection) -> Self{ Self(conn) } + } + + impl rnex_core::rmc::protocols::RemoteDisconnectable for []{ + async fn disconnect(&self){ self.0.disconnect().await; } } - impl rust_nex::rmc::protocols::HasRmcConnection for []{ - fn get_connection(&self) -> &rust_nex::rmc::protocols::RmcConnection{ + impl rnex_core::rmc::protocols::HasRmcConnection for []{ + fn get_connection(&self) -> &rnex_core::rmc::protocols::RmcConnection{ &self.0 } } @@ -202,14 +206,23 @@ impl RmcCallable for () { } } -pub trait RemoteInstantiatable{ +pub trait RmcPureRemoteObject { fn new(conn: RmcConnection) -> Self; +} + +pub trait RemoteDisconnectable { async fn disconnect(&self); } -pub struct OnlyRemote(T); +pub struct OnlyRemote(T); -impl Deref for OnlyRemote{ +impl OnlyRemote { + pub fn new(conn: RmcConnection) -> Self { + Self(T::new(conn)) + } +} + +impl Deref for OnlyRemote { type Target = T; fn deref(&self) -> &Self::Target { @@ -217,20 +230,23 @@ impl Deref for OnlyRemote{ } } -impl OnlyRemote{ - pub fn new(conn: RmcConnection) -> Self{ - Self(T::new(conn)) - } - +impl OnlyRemote { pub async fn disconnect(&self) { self.0.disconnect().await; } } -impl RmcCallable for OnlyRemote{ - fn rmc_call(&self, _responder: &SendingBufferConnection, _protocol_id: u16, _method_id: u32, _call_id: u32, _rest: Vec) -> impl Future + Send { +impl RmcCallable for OnlyRemote { + fn rmc_call( + &self, + _responder: &SendingBufferConnection, + _protocol_id: u16, + _method_id: u32, + _call_id: u32, + _rest: Vec, + ) -> impl Future + Send { // maybe respond with not implemented or something - async{} + async {} } } @@ -242,23 +258,23 @@ async fn handle_incoming( ) { let sending_conn = connection.duplicate_sender(); - while let Some(v) = connection.recv().await{ + while let Some(v) = connection.recv().await { let Some(proto_id) = v.get(4) else { error!("received too small rmc message."); error!("ending rmc gateway."); - return + return; }; // protocol 0 is hardcoded to be the no protocol protocol aka keepalive protocol - if *proto_id == 0{ + if *proto_id == 0 { println!("got keepalive"); continue; } - if (proto_id & 0x80) == 0{ + if (proto_id & 0x80) == 0 { let Some(response) = RMCResponse::new(&mut Cursor::new(v)).display_err_or_some() else { error!("ending rmc gateway."); - return + return; }; info!("got rmc response"); @@ -270,28 +286,34 @@ async fn handle_incoming( } else { let Some(message) = RMCMessage::new(&mut Cursor::new(v)).display_err_or_some() else { error!("ending rmc gateway."); - return + return; }; - let RMCMessage{ + let RMCMessage { protocol_id, method_id, call_id, - rest_of_data + rest_of_data, } = message; - info!("RMC REQUEST: Proto: {}; Method: {};", protocol_id, method_id); + info!( + "RMC REQUEST: Proto: {}; Method: {}; cid: {}", + protocol_id, method_id, call_id + ); - remote.rmc_call(&sending_conn, protocol_id, method_id, call_id, rest_of_data).await; - - + remote + .rmc_call(&sending_conn, protocol_id, method_id, call_id, rest_of_data) + .await; } } - + info!("rmc disconnected") } -pub fn new_rmc_gateway_connection(conn: SplittableBufferConnection, create_internal: F) -> Arc +pub fn new_rmc_gateway_connection( + conn: SplittableBufferConnection, + create_internal: F, +) -> Arc where F: FnOnce(RmcConnection) -> Arc, { @@ -311,18 +333,12 @@ where { let exposed_object = exposed_object.clone(); tokio::spawn(async move { - handle_incoming( - conn, - exposed_object, - notify, - incoming - ).await; + handle_incoming(conn, exposed_object, notify, incoming).await; }); - tokio::spawn(async move { - while sending_conn.is_alive(){ - sending_conn.send([0,0,0,0,0].to_vec()).await; + while sending_conn.is_alive() { + sending_conn.send([0, 0, 0, 0, 0].to_vec()).await; sleep(Duration::from_secs(10)).await; } }); @@ -331,12 +347,20 @@ where exposed_object } -impl RmcCallable for Arc{ - fn rmc_call(&self, responder: &SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec) -> impl Future + Send { - self.as_ref().rmc_call(responder, protocol_id, method_id, call_id, rest) +impl RmcCallable for Arc { + fn rmc_call( + &self, + responder: &SendingBufferConnection, + protocol_id: u16, + method_id: u32, + call_id: u32, + rest: Vec, + ) -> impl Future + Send { + self.as_ref() + .rmc_call(responder, protocol_id, method_id, call_id, rest) } } define_rmc_proto! { proto NoProto{} -} \ No newline at end of file +} diff --git a/src/rmc/protocols/nat_traversal.rs b/rnex-core/src/rmc/protocols/nat_traversal.rs similarity index 94% rename from src/rmc/protocols/nat_traversal.rs rename to rnex-core/src/rmc/protocols/nat_traversal.rs index 222e62c..c0862a8 100644 --- a/src/rmc/protocols/nat_traversal.rs +++ b/rnex-core/src/rmc/protocols/nat_traversal.rs @@ -1,5 +1,5 @@ use macros::{method_id, rmc_proto}; -use crate::rmc::response::ErrorCode; +use rnex_core::rmc::response::ErrorCode; #[rmc_proto(3)] pub trait NatTraversal{ diff --git a/rnex-core/src/rmc/protocols/nintendo_notification.rs b/rnex-core/src/rmc/protocols/nintendo_notification.rs new file mode 100644 index 0000000..ae31467 --- /dev/null +++ b/rnex-core/src/rmc/protocols/nintendo_notification.rs @@ -0,0 +1,39 @@ +use macros::{RmcSerialize, method_id, rmc_proto}; + +use rnex_core::PID; +use rnex_core::rmc::structures::any::Any; + +#[derive(RmcSerialize)] +#[rmc_struct(0)] +pub struct NintendoNotificationEvent { + pub event_type: u32, + pub sender: PID, + pub data: Any, +} + +#[derive(RmcSerialize)] +#[rmc_struct(0)] +pub struct NintendoNotificationEventGeneral { + pub param1: u32, + pub param2: u64, + pub param3: u64, + pub str_param: String, +} + +#[derive(RmcSerialize)] +#[rmc_struct(0)] +pub struct NintendoNotificationEventProfile { + pub region: u8, + pub country: u8, + pub area: u8, + pub language: u8, + pub platform: u8, +} + +#[rmc_proto(100, NoReturn)] +pub trait NintendoNotification { + #[method_id(1)] + async fn process_nintendo_notification_event_1(&self, notif: NintendoNotificationEvent); + #[method_id(2)] + async fn process_nintendo_notification_event_2(&self, notif: NintendoNotificationEvent); +} diff --git a/src/rmc/protocols/notifications.rs b/rnex-core/src/rmc/protocols/notifications.rs similarity index 60% rename from src/rmc/protocols/notifications.rs rename to rnex-core/src/rmc/protocols/notifications.rs index 164bf83..eef5c6c 100644 --- a/src/rmc/protocols/notifications.rs +++ b/rnex-core/src/rmc/protocols/notifications.rs @@ -1,19 +1,21 @@ -use macros::{method_id, rmc_proto, rmc_struct, RmcSerialize}; +use macros::{RmcSerialize, method_id, rmc_proto, rmc_struct}; -pub mod notification_types{ +use rnex_core::PID; + +pub mod notification_types { pub const OWNERSHIP_CHANGED: u32 = 4000; pub const HOST_CHANGED: u32 = 110000; } #[derive(RmcSerialize, Debug, Default, Clone)] #[rmc_struct(0)] -pub struct NotificationEvent{ - pub pid_source: u32, +pub struct NotificationEvent { + pub pid_source: PID, pub notif_type: u32, - pub param_1: u32, - pub param_2: u32, + pub param_1: PID, + pub param_2: PID, pub str_param: String, - pub param_3: u32, + pub param_3: PID, } #[rmc_proto(14, NoReturn)] @@ -21,4 +23,3 @@ pub trait Notification { #[method_id(1)] async fn process_notification_event(&self, event: NotificationEvent); } - diff --git a/rnex-core/src/rmc/protocols/ranking.rs b/rnex-core/src/rmc/protocols/ranking.rs new file mode 100644 index 0000000..b9dba06 --- /dev/null +++ b/rnex-core/src/rmc/protocols/ranking.rs @@ -0,0 +1,51 @@ +use macros::{rmc_struct, rmc_proto, RmcSerialize, method_id}; + +use rnex_core::kerberos::KerberosDateTime; +use rnex_core::rmc::structures::qbuffer::QBuffer; + +use rnex_core::rmc::response::ErrorCode; +use rnex_core::rmc::structures::ranking::UploadCompetitionData; + +#[derive(RmcSerialize, Debug, Default, Clone)] +#[rmc_struct(0)] +pub struct ResultsRange{ + pub offset: u32, + pub size: u32 +} + +#[derive(RmcSerialize, Debug, Default, Clone)] +#[rmc_struct(1)] +pub struct CompetitionRankingGetParam { + pub unk: u32, + pub range: ResultsRange, + pub festival_ids: Vec, +} + +#[derive(RmcSerialize, Debug, Default, Clone)] +#[rmc_struct(0)] +pub struct CompetitionRankingScoreInfo{ + pub fest_id: u32, + pub score_data: Vec, + pub unk: u32, + pub team_wins: Vec, + pub team_votes: Vec +} + +#[derive(RmcSerialize, Debug, Clone)] +#[rmc_struct(0)] +pub struct CompetitionRankingScoreData{ + pub unk: u32, + pub pid: u32, + pub score: u32, + pub modified: KerberosDateTime, + pub unk2: u8, + pub appdata: QBuffer +} + +#[rmc_proto(112)] +pub trait Ranking{ + #[method_id(16)] + async fn competition_ranking_get_param(&self, param: CompetitionRankingGetParam) -> Result,ErrorCode>; + #[method_id(18)] + async fn upload_competition_ranking_score(&self, param: UploadCompetitionData) -> Result; +} diff --git a/rnex-core/src/rmc/protocols/secure.rs b/rnex-core/src/rmc/protocols/secure.rs new file mode 100644 index 0000000..503bf93 --- /dev/null +++ b/rnex-core/src/rmc/protocols/secure.rs @@ -0,0 +1,24 @@ +use macros::{method_id, rmc_proto}; +use rnex_core::prudp::station_url::StationUrl; +use rnex_core::rmc::response::ErrorCode; +use rnex_core::rmc::structures::qresult::QResult; + +use crate::rmc::structures::any::Any; + +#[rmc_proto(11)] +pub trait Secure { + #[method_id(1)] + async fn register( + &self, + station_urls: Vec, + ) -> Result<(QResult, u32, StationUrl), ErrorCode>; + + #[method_id(4)] + async fn register_ex( + &self, + station_urls: Vec, + data: Any, + ) -> Result<(QResult, u32, StationUrl), ErrorCode>; + #[method_id(7)] + async fn replace_url(&self, target: StationUrl, dest: StationUrl) -> Result<(), ErrorCode>; +} diff --git a/src/rmc/response.rs b/rnex-core/src/rmc/response.rs similarity index 93% rename from src/rmc/response.rs rename to rnex-core/src/rmc/response.rs index dcdebc6..b042877 100644 --- a/src/rmc/response.rs +++ b/rnex-core/src/rmc/response.rs @@ -2,17 +2,18 @@ // attributes but this gets it to not complain anymore #![allow(unused_parens)] -use std::io; -use std::io::{Read, Seek, Write}; -use std::mem::transmute; -use bytemuck::bytes_of; -use log::error; -use v_byte_macros::EnumTryInto; -use crate::endianness::{ReadExtensions, IS_BIG_ENDIAN}; use crate::rmc::response::ErrorCode::Core_Exception; use crate::rmc::structures::qresult::ERROR_MASK; use crate::util::SendingBufferConnection; +use bytemuck::bytes_of; +use log::error; +use std::io; +use std::io::{Read, Seek, Write}; +use std::mem::transmute; +use v_byte_helpers::EnumTryInto; +use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; +#[derive(Debug, Clone)] pub enum RMCResponseResult { Success { call_id: u32, @@ -25,13 +26,14 @@ pub enum RMCResponseResult { }, } +#[derive(Debug, Clone)] pub struct RMCResponse { pub protocol_id: u8, pub response_result: RMCResponseResult, } impl RMCResponse { - pub fn new(stream: &mut (impl Seek + Read)) -> io::Result{ + pub fn new(stream: &mut (impl Seek + Read)) -> io::Result { // ignore the size for now this will only be used for checking let size: u32 = stream.read_struct(IS_BIG_ENDIAN)?; @@ -46,7 +48,7 @@ impl RMCResponse { let is_success: u8 = stream.read_struct(IS_BIG_ENDIAN)?; - let response_result = if is_success == 0x01{ + let response_result = if is_success == 0x01 { let call_id: u32 = stream.read_struct(IS_BIG_ENDIAN)?; let method_id: u32 = stream.read_struct(IS_BIG_ENDIAN)?; let method_id = method_id & (!0x8000); @@ -55,11 +57,10 @@ impl RMCResponse { stream.read(&mut data)?; - RMCResponseResult::Success { call_id, method_id, - data + data, } } else { let error_code: u32 = stream.read_struct(IS_BIG_ENDIAN)?; @@ -68,7 +69,7 @@ impl RMCResponse { RMCResponseResult::Error { error_code: { - match ErrorCode::try_from(error_code){ + match ErrorCode::try_from(error_code) { Ok(v) => v, Err(()) => { error!("invalid error code {:#010x}", error_code); @@ -77,36 +78,35 @@ impl RMCResponse { } }, call_id, - } }; - Ok(Self{ + Ok(Self { protocol_id, - response_result + response_result, }) } - pub fn get_call_id(&self) -> u32{ - match &self.response_result{ - RMCResponseResult::Success { call_id, ..} => *call_id, - RMCResponseResult::Error { call_id, .. } => *call_id + pub fn get_call_id(&self) -> u32 { + match &self.response_result { + RMCResponseResult::Success { call_id, .. } => *call_id, + RMCResponseResult::Error { call_id, .. } => *call_id, } } pub fn to_data(self) -> Vec { - generate_response(self.protocol_id, self.response_result).expect("failed to generate response") + generate_response(self.protocol_id, self.response_result) + .expect("failed to generate response") } } pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Result> { - let size = 1 + 1 + match &response { - RMCResponseResult::Success { - data, - .. - } => 4 + 4 + data.len(), - RMCResponseResult::Error { .. } => 4 + 4, - }; + let size = 1 + + 1 + + match &response { + RMCResponseResult::Success { data, .. } => 4 + 4 + data.len(), + RMCResponseResult::Error { .. } => 4 + 4, + }; let mut data_out = Vec::with_capacity(size + 4); @@ -119,7 +119,7 @@ pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Re RMCResponseResult::Success { call_id, method_id, - data + data, } => { data_out.push(1); data_out.write_all(bytes_of(&call_id))?; @@ -129,7 +129,7 @@ pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Re } RMCResponseResult::Error { call_id, - error_code + error_code, } => { data_out.push(0); let error_code_val: u32 = error_code.into(); @@ -151,23 +151,21 @@ pub async fn send_result( method_id: u32, call_id: u32, ) { - let response_result = match result { Ok(v) => RMCResponseResult::Success { call_id, method_id, - data: v + data: v, + }, + Err(e) => RMCResponseResult::Error { + call_id, + error_code: e.into(), }, - Err(e) => - RMCResponseResult::Error { - call_id, - error_code: e.into() - } }; - let response = RMCResponse{ + let response = RMCResponse { response_result, - protocol_id + protocol_id, }; send_response(connection, response).await @@ -177,7 +175,6 @@ pub async fn send_response(connection: &SendingBufferConnection, rmcresponse: RM connection.send(rmcresponse.to_data()).await; } - //taken from kinnays error list directly #[allow(nonstandard_style)] #[repr(u32)] @@ -464,25 +461,23 @@ impl Into for ErrorCode { #[cfg(test)] mod test { - use hmac::digest::consts::U5; - use hmac::digest::KeyInit; - use rc4::{Rc4, StreamCipher}; use crate::rmc::response::ErrorCode; + use hmac::digest::KeyInit; + use hmac::digest::consts::U5; + use rc4::{Rc4, StreamCipher}; #[test] fn test() { - let mut data_orig = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 69, 4, 20]; + let data_orig = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 69, 4, 20]; let mut data = data_orig; - let mut rc4: Rc4 = - Rc4::new_from_slice("FUCKE".as_bytes().into()).expect("invalid key"); + let mut rc4: Rc4 = Rc4::new_from_slice("FUCKE".as_bytes().into()).expect("invalid key"); rc4.apply_keystream(&mut data); assert_ne!(data_orig, data); - let mut rc4: Rc4 = - Rc4::new_from_slice("FUCKE".as_bytes().into()).expect("invalid key"); + let mut rc4: Rc4 = Rc4::new_from_slice("FUCKE".as_bytes().into()).expect("invalid key"); rc4.apply_keystream(&mut data); @@ -494,4 +489,4 @@ mod test { let val: u32 = ErrorCode::Core_Unknown.into(); assert_eq!(val, 0x00010001) } -} \ No newline at end of file +} diff --git a/rnex-core/src/rmc/structures/any.rs b/rnex-core/src/rmc/structures/any.rs new file mode 100644 index 0000000..0f531d8 --- /dev/null +++ b/rnex-core/src/rmc/structures/any.rs @@ -0,0 +1,45 @@ +use rnex_core::rmc::structures::{Result, RmcSerialize}; +use std::io::{Cursor, Read, Write}; +use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; + +#[derive(Debug, Default, Clone)] +pub struct Any { + pub name: String, + pub data: Vec, +} + +impl RmcSerialize for Any { + fn serialize(&self, writer: &mut impl Write) -> Result<()> { + self.name.serialize(writer)?; + + let u32_len = self.data.len() as u32; + (u32_len + 4).serialize(writer)?; + self.data.serialize(writer)?; + + Ok(()) + } + fn deserialize(reader: &mut impl Read) -> Result { + let name = String::deserialize(reader)?; + + // also length ? + let _len2: u32 = reader.read_struct(IS_BIG_ENDIAN)?; + let data = Vec::deserialize(reader)?; + + Ok(Any { name, data }) + } +} + +impl Any { + pub fn try_get(&self) -> Option> { + if self.name != T::name() { + return None; + } + return Some(T::deserialize(&mut Cursor::new(&self.data[..]))); + } + pub fn new(val: &T) -> Result { + return Ok(Self { + name: T::name().to_owned(), + data: val.to_data()?, + }); + } +} diff --git a/rnex-core/src/rmc/structures/buffer.rs b/rnex-core/src/rmc/structures/buffer.rs new file mode 100644 index 0000000..c147133 --- /dev/null +++ b/rnex-core/src/rmc/structures/buffer.rs @@ -0,0 +1,36 @@ +use crate::rmc::structures::Result; +use crate::rmc::structures::RmcSerialize; +use std::io::{Read, Write}; + +impl<'a> RmcSerialize for &'a [u8] { + fn serialize(&self, writer: &mut impl Write) -> Result<()> { + let u32_size = self.len() as u32; + writer.write(bytemuck::bytes_of(&u32_size))?; + writer.write(self)?; + + Ok(()) + } + + /// DO NOT USE (also maybe split off the serialize and deserialize functions at some point) + fn deserialize(_reader: &mut impl Read) -> Result { + panic!("cannot deserialize to a u8 slice reference (use this ONLY for writing)") + } + + fn serialize_write_size(&self) -> Result { + Ok(4 + self.len() as u32) + } +} + +impl RmcSerialize for Box<[u8]> { + fn serialize(&self, writer: &mut impl Write) -> Result<()> { + (&self[..]).serialize(writer) + } + + fn deserialize(reader: &mut impl Read) -> Result { + Vec::deserialize(reader).map(|v| v.into_boxed_slice()) + } + + fn serialize_write_size(&self) -> Result { + (&self[..]).serialize_write_size() + } +} diff --git a/rnex-core/src/rmc/structures/connection_data.rs b/rnex-core/src/rmc/structures/connection_data.rs new file mode 100644 index 0000000..d134e85 --- /dev/null +++ b/rnex-core/src/rmc/structures/connection_data.rs @@ -0,0 +1,19 @@ +use macros::RmcSerialize; +use rnex_core::kerberos::KerberosDateTime; + +#[derive(Debug, RmcSerialize)] +#[rmc_struct(1)] +pub struct ConnectionData { + pub station_url: String, + pub special_protocols: Vec, + pub special_station_url: String, + pub date_time: KerberosDateTime, +} + +#[derive(Debug, RmcSerialize)] +#[rmc_struct(1)] +pub struct ConnectionDataOld { + pub station_url: String, + pub special_protocols: Vec, + pub special_station_url: String, +} diff --git a/rnex-core/src/rmc/structures/data.rs b/rnex-core/src/rmc/structures/data.rs new file mode 100644 index 0000000..c6064ae --- /dev/null +++ b/rnex-core/src/rmc/structures/data.rs @@ -0,0 +1,5 @@ +use macros::RmcSerialize; + +#[derive(RmcSerialize, Debug, Clone, Copy)] +#[rmc_struct(0)] +pub struct Data {} diff --git a/rnex-core/src/rmc/structures/helpers.rs b/rnex-core/src/rmc/structures/helpers.rs new file mode 100644 index 0000000..eeadbd6 --- /dev/null +++ b/rnex-core/src/rmc/structures/helpers.rs @@ -0,0 +1,48 @@ +use std::{fmt, io}; + +pub struct DummyFormatWriter(u32); + +impl fmt::Write for DummyFormatWriter{ + fn write_str(&mut self, s: &str) -> fmt::Result { + self.0 += s.as_bytes().len() as u32; + Ok(()) + } +} + +impl DummyFormatWriter{ + pub const fn new() -> Self{ Self(0) } + pub const fn serialize_str_len(&self) -> u32 { + 2 + self.0 + 1 + } +} + +pub struct DummyWriter(u32); + +impl io::Write for DummyWriter{ + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0 += buf.len() as u32; + Ok(buf.len()) + } + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.0 += buf.len() as u32; + Ok(()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl DummyWriter{ + pub const fn new() -> Self{ Self(0) } + pub const fn get_total_len(&self) -> u32{ + self.0 + } +} + + +pub fn len_of_write(f: impl FnOnce(&mut DummyWriter) -> anyhow::Result<()>) -> u32{ + let mut dummy = DummyWriter::new(); + f(&mut dummy).ok(); + dummy.get_total_len() +} \ No newline at end of file diff --git a/rnex-core/src/rmc/structures/list.rs b/rnex-core/src/rmc/structures/list.rs new file mode 100644 index 0000000..7c7db8e --- /dev/null +++ b/rnex-core/src/rmc/structures/list.rs @@ -0,0 +1,74 @@ +use crate::rmc::structures::RmcSerialize; +use bytemuck::bytes_of; +use std::io::{Read, Write}; +use std::mem::MaybeUninit; +use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; + +// this is also for implementing `Buffer` this is tecnically not the same as its handled internaly +// probably but as it has the same mapping it doesn't matter and simplifies things +impl RmcSerialize for Vec { + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + let u32_len = self.len() as u32; + + writer.write_all(bytes_of(&u32_len))?; + for e in self { + e.serialize(writer)?; + } + + Ok(()) + } + + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + println!("reading list"); + let len: u32 = reader.read_struct(IS_BIG_ENDIAN)?; + + println!("readijg list: {:?}", len); + //let mut vec = Vec::with_capacity(len as usize); + + let vec: Vec = (0..len) + .map(|_| T::deserialize(reader)) + .collect::, _>>()?; + + Ok(vec) + } + + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + let mut val = 0u32; + for i in self { + val += i.serialize_write_size()?; + } + Ok(4 + val) + } +} + +impl RmcSerialize for [T; LEN] { + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + for i in 0..LEN { + self[i].serialize(writer)?; + } + + Ok(()) + } + + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + let mut arr = [const { MaybeUninit::::uninit() }; LEN]; + + for i in 0..LEN { + arr[i] = MaybeUninit::new(T::deserialize(reader)?); + } + + // all of the elements are now initialized so it is safe to assume they are initialized + + let arr = arr.map(|v| unsafe { v.assume_init() }); + + Ok(arr) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + let mut val = 0u32; + for i in self { + val += i.serialize_write_size()?; + } + Ok(val) + } +} diff --git a/src/rmc/structures/matchmake.rs b/rnex-core/src/rmc/structures/matchmake.rs similarity index 92% rename from src/rmc/structures/matchmake.rs rename to rnex-core/src/rmc/structures/matchmake.rs index 28e851b..a1518d1 100644 --- a/src/rmc/structures/matchmake.rs +++ b/rnex-core/src/rmc/structures/matchmake.rs @@ -1,14 +1,16 @@ -use crate::kerberos::KerberosDateTime; -use crate::rmc::structures::variant::Variant; use macros::RmcSerialize; +use rnex_core::kerberos::KerberosDateTime; +use rnex_core::rmc::structures::variant::Variant; + +use rnex_core::PID; // rmc structure #[derive(RmcSerialize, Debug, Clone, Default)] #[rmc_struct(0)] pub struct Gathering { pub self_gid: u32, - pub owner_pid: u32, - pub host_pid: u32, + pub owner_pid: PID, + pub host_pid: PID, pub minimum_participants: u16, pub maximum_participants: u16, pub participant_policy: u32, @@ -73,7 +75,7 @@ pub struct MatchmakeSessionSearchCriteria { #[rmc_struct(0)] pub struct AutoMatchmakeParam { pub matchmake_session: MatchmakeSession, - pub additional_participants: Vec, + pub additional_participants: Vec, pub gid_for_participation_check: u32, pub auto_matchmake_option: u32, pub join_message: String, @@ -86,7 +88,7 @@ pub struct AutoMatchmakeParam { #[rmc_struct(0)] pub struct CreateMatchmakeSessionParam { pub matchmake_session: MatchmakeSession, - pub additional_participants: Vec, + pub additional_participants: Vec, pub gid_for_participation_check: u32, pub create_matchmake_session_option: u32, pub join_message: String, @@ -103,7 +105,7 @@ pub struct MatchmakeBlockListParam { #[rmc_struct(0)] pub struct JoinMatchmakeSessionParam { pub gid: u32, - pub additional_participants: Vec, + pub additional_participants: Vec, pub gid_for_participation_check: u32, pub join_matchmake_session_open: u32, pub join_matchmake_session_behavior: u8, diff --git a/rnex-core/src/rmc/structures/mod.rs b/rnex-core/src/rmc/structures/mod.rs new file mode 100644 index 0000000..35dce08 --- /dev/null +++ b/rnex-core/src/rmc/structures/mod.rs @@ -0,0 +1,80 @@ +use crate::rmc::structures::helpers::DummyWriter; +use std::io::{Read, Write}; +use std::string::FromUtf8Error; +use std::{fmt, io}; +use thiserror::Error; +//ideas for the future: make a proc macro library which allows generation of struct reads + +#[derive(Error, Debug)] +pub enum Error { + #[error("Io Error: {0}")] + Io(#[from] io::Error), + #[error("UTF8 conversion Error: {0}")] + Utf8(#[from] FromUtf8Error), + #[error("unexpected value: {0}")] + UnexpectedValue(u64), + #[error("version mismatch: {0}")] + VersionMismatch(u8), + #[error("an error occurred reading the station url")] + StationUrlInvalid, + #[error("error formatting text: {0}")] + FormatError(#[from] fmt::Error), +} + +pub type Result = std::result::Result; + +pub mod any; +pub mod buffer; +pub mod connection_data; +pub mod data; +pub mod helpers; +pub mod list; +pub mod matchmake; +pub mod networking; +pub mod primitives; +pub mod qbuffer; +pub mod qresult; +pub mod ranking; +pub mod rmc_struct; +pub mod string; +pub mod variant; + +pub trait RmcSerialize { + fn serialize(&self, writer: &mut impl Write) -> Result<()>; + fn serialize_write_size(&self) -> Result { + let mut dummy = DummyWriter::new(); + + self.serialize(&mut dummy)?; + + Ok(dummy.get_total_len()) + } + fn deserialize(reader: &mut impl Read) -> Result + where + Self: Sized; + + fn to_data(&self) -> Result> { + let expected_size = self.serialize_write_size()?; + let mut data = Vec::with_capacity(expected_size as usize); + + self.serialize(&mut data)?; + + debug_assert_eq!(expected_size, data.len() as u32); + + Ok(data) + } + fn name() -> &'static str { + "NoNameSpecified" + } +} + +impl RmcSerialize for () { + fn serialize(&self, _writer: &mut impl Write) -> Result<()> { + Ok(()) + } + fn deserialize(_reader: &mut impl Read) -> Result { + Ok(()) + } + fn serialize_write_size(&self) -> Result { + Ok(0) + } +} diff --git a/rnex-core/src/rmc/structures/networking.rs b/rnex-core/src/rmc/structures/networking.rs new file mode 100644 index 0000000..bc95b9e --- /dev/null +++ b/rnex-core/src/rmc/structures/networking.rs @@ -0,0 +1,93 @@ +use crate::rmc::structures::{Error, Result, RmcSerialize}; +use rnex_core::prudp::virtual_port::VirtualPort; +use std::io::{Read, Write}; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; +use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; + +impl RmcSerialize for SocketAddr { + fn deserialize(reader: &mut impl std::io::Read) -> Result + where + Self: Sized, + { + let val: u8 = reader.read_struct(IS_BIG_ENDIAN)?; + match val { + 4 => Ok(SocketAddr::V4(SocketAddrV4::deserialize(reader)?)), + 6 => Ok(SocketAddr::V6(SocketAddrV6::deserialize(reader)?)), + v => Err(Error::UnexpectedValue(v as u64)), + } + } + fn serialize(&self, writer: &mut impl Write) -> Result<()> { + match self { + SocketAddr::V4(v) => { + writer.write_all(&[4])?; + v.serialize(writer)?; + } + SocketAddr::V6(v) => { + writer.write_all(&[6])?; + v.serialize(writer)?; + } + } + Ok(()) + } +} + +impl RmcSerialize for SocketAddrV4 { + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + self.ip().to_bits().serialize(writer)?; + self.port().serialize(writer)?; + + Ok(()) + } + + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + let ip = u32::deserialize(reader)?; + let port = u16::deserialize(reader)?; + + Ok(SocketAddrV4::new(Ipv4Addr::from_bits(ip), port)) + } + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(6) + } +} +impl RmcSerialize for SocketAddrV6 { + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + self.ip().to_bits().serialize(writer)?; + self.port().serialize(writer)?; + self.flowinfo().serialize(writer)?; + self.scope_id().serialize(writer)?; + + Ok(()) + } + + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + let ip = u128::deserialize(reader)?; + let port = u16::deserialize(reader)?; + let flowinfo = u32::deserialize(reader)?; + let scope_id = u32::deserialize(reader)?; + + Ok(SocketAddrV6::new( + Ipv6Addr::from_bits(ip), + port, + flowinfo, + scope_id, + )) + } + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(6) + } +} + +impl RmcSerialize for VirtualPort { + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + self.0.serialize(writer)?; + + Ok(()) + } + + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + Ok(Self(u8::deserialize(reader)?)) + } + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(1) + } +} diff --git a/rnex-core/src/rmc/structures/primitives.rs b/rnex-core/src/rmc/structures/primitives.rs new file mode 100644 index 0000000..6ce1c2f --- /dev/null +++ b/rnex-core/src/rmc/structures/primitives.rs @@ -0,0 +1,496 @@ +use crate::rmc::structures::RmcSerialize; +use bytemuck::{bytes_of, bytes_of_mut}; +use std::io::{Read, Write}; +use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; + +impl RmcSerialize for u8 { + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + Ok(writer.write_all(bytes_of(self))?) + } + + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + Ok(reader.read_struct(IS_BIG_ENDIAN)?) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(1) + } +} + +impl RmcSerialize for i8 { + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + Ok(writer.write_all(bytes_of(self))?) + } + + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + Ok(reader.read_struct(IS_BIG_ENDIAN)?) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(1) + } +} + +impl RmcSerialize for u16 { + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + Ok(writer.write_all(bytes_of(self))?) + } + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + Ok(reader.read_struct(IS_BIG_ENDIAN)?) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(2) + } +} + +impl RmcSerialize for i16 { + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + Ok(writer.write_all(bytes_of(self))?) + } + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + Ok(reader.read_struct(IS_BIG_ENDIAN)?) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(2) + } +} + +impl RmcSerialize for u32 { + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + Ok(writer.write_all(bytes_of(self))?) + } + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + Ok(reader.read_struct(IS_BIG_ENDIAN)?) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(4) + } +} + +impl RmcSerialize for i32 { + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + Ok(writer.write_all(bytes_of(self))?) + } + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + Ok(reader.read_struct(IS_BIG_ENDIAN)?) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(4) + } +} + +impl RmcSerialize for u64 { + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + Ok(writer.write_all(bytes_of(self))?) + } + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + Ok(reader.read_struct(IS_BIG_ENDIAN)?) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(8) + } +} + +impl RmcSerialize for u128 { + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + Ok(writer.write_all(bytes_of(self))?) + } + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + let mut data = 0u128; + reader.read_exact(&mut bytes_of_mut(&mut data))?; + Ok(data) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(8) + } +} + +impl RmcSerialize for i64 { + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + Ok(writer.write_all(bytes_of(self))?) + } + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + Ok(reader.read_struct(IS_BIG_ENDIAN)?) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(8) + } +} + +impl RmcSerialize for f64 { + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + Ok(writer.write_all(bytes_of(self))?) + } + + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + Ok(reader.read_struct(IS_BIG_ENDIAN)?) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(8) + } +} + +impl RmcSerialize for bool { + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + match self { + true => writer.write_all(&[1])?, + false => writer.write_all(&[0])?, + } + Ok(()) + } + + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + Ok(u8::deserialize(reader)? != 0) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(1) + } +} + +impl RmcSerialize for (T, U) { + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + self.0.serialize(writer)?; + self.1.serialize(writer)?; + Ok(()) + } + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + let first = T::deserialize(reader)?; + let second = U::deserialize(reader)?; + + Ok((first, second)) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(self.0.serialize_write_size()? + self.1.serialize_write_size()?) + } +} + +impl RmcSerialize for (T, U, V) { + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + self.0.serialize(writer)?; + self.1.serialize(writer)?; + self.2.serialize(writer)?; + Ok(()) + } + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + let first = T::deserialize(reader)?; + let second = U::deserialize(reader)?; + let third = V::deserialize(reader)?; + + Ok((first, second, third)) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(self.0.serialize_write_size()? + + self.1.serialize_write_size()? + + self.2.serialize_write_size()?) + } +} + +impl RmcSerialize + for (T, U, V, W) +{ + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + self.0.serialize(writer)?; + self.1.serialize(writer)?; + self.2.serialize(writer)?; + self.3.serialize(writer)?; + Ok(()) + } + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + let first = T::deserialize(reader)?; + let second = U::deserialize(reader)?; + let third = V::deserialize(reader)?; + let fourth = W::deserialize(reader)?; + + Ok((first, second, third, fourth)) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(self.0.serialize_write_size()? + + self.1.serialize_write_size()? + + self.2.serialize_write_size()? + + self.3.serialize_write_size()?) + } +} + +impl + RmcSerialize for (T, U, V, W, X) +{ + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + self.0.serialize(writer)?; + self.1.serialize(writer)?; + self.2.serialize(writer)?; + self.3.serialize(writer)?; + self.4.serialize(writer)?; + + Ok(()) + } + + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + let first = T::deserialize(reader)?; + let second = U::deserialize(reader)?; + let third = V::deserialize(reader)?; + let fourth = W::deserialize(reader)?; + let fifth = X::deserialize(reader)?; + + Ok((first, second, third, fourth, fifth)) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(self.0.serialize_write_size()? + + self.1.serialize_write_size()? + + self.2.serialize_write_size()? + + self.3.serialize_write_size()? + + self.4.serialize_write_size()?) + } +} + +impl< + T: RmcSerialize, + U: RmcSerialize, + V: RmcSerialize, + W: RmcSerialize, + X: RmcSerialize, + Y: RmcSerialize, +> RmcSerialize for (T, U, V, W, X, Y) +{ + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + self.0.serialize(writer)?; + self.1.serialize(writer)?; + self.2.serialize(writer)?; + self.3.serialize(writer)?; + self.4.serialize(writer)?; + self.5.serialize(writer)?; + + Ok(()) + } + + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + let first = T::deserialize(reader)?; + let second = U::deserialize(reader)?; + let third = V::deserialize(reader)?; + let fourth = W::deserialize(reader)?; + let fifth = X::deserialize(reader)?; + let sixth = Y::deserialize(reader)?; + + Ok((first, second, third, fourth, fifth, sixth)) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(self.0.serialize_write_size()? + + self.1.serialize_write_size()? + + self.2.serialize_write_size()? + + self.3.serialize_write_size()? + + self.4.serialize_write_size()? + + self.5.serialize_write_size()?) + } +} + +impl< + T: RmcSerialize, + U: RmcSerialize, + V: RmcSerialize, + W: RmcSerialize, + X: RmcSerialize, + Y: RmcSerialize, + Z: RmcSerialize, +> RmcSerialize for (T, U, V, W, X, Y, Z) +{ + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + self.0.serialize(writer)?; + self.1.serialize(writer)?; + self.2.serialize(writer)?; + self.3.serialize(writer)?; + self.4.serialize(writer)?; + self.5.serialize(writer)?; + self.6.serialize(writer)?; + + Ok(()) + } + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + let first = T::deserialize(reader)?; + let second = U::deserialize(reader)?; + let third = V::deserialize(reader)?; + let fourth = W::deserialize(reader)?; + let fifth = X::deserialize(reader)?; + let sixth = Y::deserialize(reader)?; + let seventh = Z::deserialize(reader)?; + + Ok((first, second, third, fourth, fifth, sixth, seventh)) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(self.0.serialize_write_size()? + + self.1.serialize_write_size()? + + self.2.serialize_write_size()? + + self.3.serialize_write_size()? + + self.4.serialize_write_size()? + + self.5.serialize_write_size()? + + self.6.serialize_write_size()?) + } +} + +impl< + T: RmcSerialize, + U: RmcSerialize, + V: RmcSerialize, + W: RmcSerialize, + X: RmcSerialize, + Y: RmcSerialize, + Z: RmcSerialize, + A: RmcSerialize, +> RmcSerialize for (T, U, V, W, X, Y, Z, A) +{ + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + self.0.serialize(writer)?; + self.1.serialize(writer)?; + self.2.serialize(writer)?; + self.3.serialize(writer)?; + self.4.serialize(writer)?; + self.5.serialize(writer)?; + self.6.serialize(writer)?; + self.7.serialize(writer)?; + + Ok(()) + } + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + let first = T::deserialize(reader)?; + let second = U::deserialize(reader)?; + let third = V::deserialize(reader)?; + let fourth = W::deserialize(reader)?; + let fifth = X::deserialize(reader)?; + let sixth = Y::deserialize(reader)?; + let seventh = Z::deserialize(reader)?; + let eighth = A::deserialize(reader)?; + + Ok((first, second, third, fourth, fifth, sixth, seventh, eighth)) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(self.0.serialize_write_size()? + + self.1.serialize_write_size()? + + self.2.serialize_write_size()? + + self.3.serialize_write_size()? + + self.4.serialize_write_size()? + + self.5.serialize_write_size()? + + self.6.serialize_write_size()? + + self.7.serialize_write_size()?) + } +} + +impl< + T: RmcSerialize, + U: RmcSerialize, + V: RmcSerialize, + W: RmcSerialize, + X: RmcSerialize, + Y: RmcSerialize, + Z: RmcSerialize, + A: RmcSerialize, + B: RmcSerialize, +> RmcSerialize for (T, U, V, W, X, Y, Z, A, B) +{ + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + self.0.serialize(writer)?; + self.1.serialize(writer)?; + self.2.serialize(writer)?; + self.3.serialize(writer)?; + self.4.serialize(writer)?; + self.5.serialize(writer)?; + self.6.serialize(writer)?; + self.7.serialize(writer)?; + self.8.serialize(writer)?; + + Ok(()) + } + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + let first = T::deserialize(reader)?; + let second = U::deserialize(reader)?; + let third = V::deserialize(reader)?; + let fourth = W::deserialize(reader)?; + let fifth = X::deserialize(reader)?; + let sixth = Y::deserialize(reader)?; + let seventh = Z::deserialize(reader)?; + let eighth = A::deserialize(reader)?; + let nineth = B::deserialize(reader)?; + + Ok(( + first, second, third, fourth, fifth, sixth, seventh, eighth, nineth, + )) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(self.0.serialize_write_size()? + + self.1.serialize_write_size()? + + self.2.serialize_write_size()? + + self.3.serialize_write_size()? + + self.4.serialize_write_size()? + + self.5.serialize_write_size()? + + self.6.serialize_write_size()? + + self.7.serialize_write_size()? + + self.8.serialize_write_size()?) + } +} + +impl RmcSerialize for Box { + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + self.as_ref().serialize(writer) + } + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + T::deserialize(reader).map(Box::new) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + T::serialize_write_size(self.as_ref()) + } +} diff --git a/src/rmc/structures/qbuffer.rs b/rnex-core/src/rmc/structures/qbuffer.rs similarity index 70% rename from src/rmc/structures/qbuffer.rs rename to rnex-core/src/rmc/structures/qbuffer.rs index bb6c235..2f7b23a 100644 --- a/src/rmc/structures/qbuffer.rs +++ b/rnex-core/src/rmc/structures/qbuffer.rs @@ -1,14 +1,13 @@ use std::io::{Read, Write}; use bytemuck::bytes_of; -use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; +use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; use crate::rmc::structures::{Result, RmcSerialize}; - -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct QBuffer(pub Vec); impl RmcSerialize for QBuffer{ - fn serialize(&self, writer: &mut dyn Write) -> Result<()> { + fn serialize(&self, writer: &mut impl Write) -> Result<()> { let len_u16 = self.0.len() as u16; writer.write(bytes_of(&len_u16))?; @@ -17,7 +16,7 @@ impl RmcSerialize for QBuffer{ Ok(()) } - fn deserialize(mut reader: &mut dyn Read) -> Result { + fn deserialize(reader: &mut impl Read) -> Result { let size: u16 = reader.read_struct(IS_BIG_ENDIAN)?; let mut vec = vec![0; size as usize]; diff --git a/rnex-core/src/rmc/structures/qresult.rs b/rnex-core/src/rmc/structures/qresult.rs new file mode 100644 index 0000000..13b79d7 --- /dev/null +++ b/rnex-core/src/rmc/structures/qresult.rs @@ -0,0 +1,42 @@ +use crate::rmc::response::ErrorCode; +use crate::rmc::structures::{Result, RmcSerialize}; +use bytemuck::{Pod, Zeroable, bytes_of}; +use std::io::{Read, Write}; +use v_byte_helpers::SwapEndian; +use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; + +pub const ERROR_MASK: u32 = 1 << 31; + +#[derive(Pod, Zeroable, Copy, Clone, SwapEndian, Debug)] +#[repr(transparent)] +pub struct QResult(u32); + +impl QResult { + pub fn success(error_code: ErrorCode) -> Self { + let val: u32 = error_code.into(); + + Self(val & (!ERROR_MASK)) + } + + pub fn error(error_code: ErrorCode) -> Self { + let val: u32 = error_code.into(); + + Self(val | ERROR_MASK) + } +} + +impl RmcSerialize for QResult { + fn serialize(&self, writer: &mut impl Write) -> Result<()> { + writer.write(bytes_of(self))?; + Ok(()) + } + + fn deserialize(reader: &mut impl Read) -> Result { + let result: Self = reader.read_struct(IS_BIG_ENDIAN)?; + println!("reading qresult: {:x}", result.0); + Ok(result) + } + fn serialize_write_size(&self) -> Result { + Ok(4) + } +} diff --git a/rnex-core/src/rmc/structures/ranking.rs b/rnex-core/src/rmc/structures/ranking.rs new file mode 100644 index 0000000..773889d --- /dev/null +++ b/rnex-core/src/rmc/structures/ranking.rs @@ -0,0 +1,22 @@ +use bytemuck::{Pod, Zeroable}; +use macros::RmcSerialize; +use rnex_core::rmc::structures::qbuffer::QBuffer; + +#[derive(RmcSerialize, Debug)] +#[rmc_struct(0)] +pub struct UploadCompetitionData{ + pub unk_1/*?*/: u32, + pub splatfest_id: u32, + pub unk_2/*?*/: u32, + pub score: u32, + pub team_id: u8, + pub team_win: u8, + pub is_first_upload: bool, + pub appdata: QBuffer, +} + +#[derive(Copy, Clone, Pod, Zeroable)] +#[repr(C)] +struct UserData { + name: [u16; 0x10], +} diff --git a/rnex-core/src/rmc/structures/rmc_struct.rs b/rnex-core/src/rmc/structures/rmc_struct.rs new file mode 100644 index 0000000..7660f96 --- /dev/null +++ b/rnex-core/src/rmc/structures/rmc_struct.rs @@ -0,0 +1,129 @@ +use crate::rmc::structures::Result; +use std::cmp::max; +use std::fmt::Arguments; +use std::io; +use std::io::{ErrorKind, IoSlice, Read, Write}; + +#[repr(C, packed)] +struct StructureHeader { + version: u8, + length: u32, +} + +#[cfg(feature = "rmc_struct_header")] +pub const HEADER_SIZE: u32 = 0; +#[cfg(not(feature = "rmc_struct_header"))] +pub const HEADER_SIZE: u32 = 5; + +pub struct OnlyWriteVec<'a>(&'a mut Vec); + +impl Write for OnlyWriteVec<'_> { + fn flush(&mut self) -> io::Result<()> { + self.0.flush() + } + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.0.write_all(buf) + } + fn write_fmt(&mut self, args: Arguments<'_>) -> io::Result<()> { + self.0.write_fmt(args) + } + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } +} + +#[cfg(feature = "rmc_struct_header")] +pub fn write_struct( + writer: &mut T, + version: u8, + inner_size: u32, + pred: impl FnOnce(&mut T) -> Result<()>, +) -> Result<()> { + use bytemuck::bytes_of; + + writer.write_all(&[version])?; + + writer.write_all(bytes_of(&inner_size))?; + + (pred)(writer)?; + + Ok(()) +} + +#[cfg(not(feature = "rmc_struct_header"))] +pub fn write_struct( + writer: &mut T, + _version: u8, + _inner_size: u32, + pred: impl FnOnce(&mut T) -> Result<()>, +) -> Result<()> { + pred(writer) +} + +pub struct SubRead<'a, T: Read> { + left_to_read: usize, + origin: &'a mut T, +} + +impl<'a, T: Read> SubRead<'a, T> { + pub const fn new(origin: &'a mut T, left_to_read: usize) -> Self { + Self { + left_to_read, + origin, + } + } +} + +impl Read for SubRead<'_, T> { + #[inline(always)] + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let max_read = max(self.left_to_read, buf.len()); + let read = self.origin.read(&mut buf[..max_read])?; + self.left_to_read -= read; + Ok(read) + } + + #[inline(always)] + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + if buf.len() > self.left_to_read { + return Err(io::Error::new( + ErrorKind::UnexpectedEof, + "Would run over end of SubRead", + )); + } + self.left_to_read -= buf.len(); + self.origin.read_exact(buf) + } +} + +#[cfg(feature = "rmc_struct_header")] +pub fn read_struct( + reader: &mut R, + version: u8, + pred: impl FnOnce(&mut SubRead) -> Result, +) -> Result { + use crate::rmc::structures::Error::VersionMismatch; + use v_byte_helpers::IS_BIG_ENDIAN; + use v_byte_helpers::ReadExtensions; + let ver: u8 = reader.read_struct(IS_BIG_ENDIAN)?; + + if ver != version { + return Err(VersionMismatch(ver)); + } + + let size: u32 = reader.read_struct(IS_BIG_ENDIAN)?; + + Ok(pred(&mut SubRead::new(reader, size as usize))?) +} + +#[cfg(not(feature = "rmc_struct_header"))] +pub fn read_struct( + mut reader: &mut R, + _version: u8, + pred: impl FnOnce(&mut R) -> Result, +) -> Result { + Ok(pred(&mut reader)?) +} diff --git a/rnex-core/src/rmc/structures/string.rs b/rnex-core/src/rmc/structures/string.rs new file mode 100644 index 0000000..8830822 --- /dev/null +++ b/rnex-core/src/rmc/structures/string.rs @@ -0,0 +1,47 @@ +use super::{Result, RmcSerialize}; +use bytemuck::bytes_of; +use log::error; +use std::io::{Read, Write}; +use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; + +impl RmcSerialize for String { + fn deserialize(reader: &mut impl Read) -> Result { + let len: u16 = reader.read_struct(IS_BIG_ENDIAN)?; + if len == 0 { + return Ok("".to_string()); + } + let mut data = vec![0; len as usize]; + reader.read_exact(&mut data)?; + if *data.last().unwrap() != 0 { + error!("unable to find null terminator... continuing anyways"); + } + data.pop(); + + Ok(String::from_utf8(data)?) + } + fn serialize(&self, writer: &mut impl Write) -> Result<()> { + (&self[..]).serialize(writer) + } + fn serialize_write_size(&self) -> Result { + (&self[..]).serialize_write_size() + } +} + +impl RmcSerialize for &str { + fn deserialize(_reader: &mut impl Read) -> Result { + panic!("cannot serialize to &str") + } + fn serialize(&self, writer: &mut impl Write) -> Result<()> { + let u16_len: u16 = (self.len() + 1) as u16; + writer.write_all(bytes_of(&u16_len))?; + + writer.write_all(self.as_bytes())?; + writer.write_all(&[0])?; + + Ok(()) + } + #[inline(always)] + fn serialize_write_size(&self) -> Result { + Ok(2 + self.as_bytes().len() as u32 + 1) + } +} diff --git a/src/rmc/structures/variant.rs b/rnex-core/src/rmc/structures/variant.rs similarity index 80% rename from src/rmc/structures/variant.rs rename to rnex-core/src/rmc/structures/variant.rs index ebd25b8..9f705d0 100644 --- a/src/rmc/structures/variant.rs +++ b/rnex-core/src/rmc/structures/variant.rs @@ -1,10 +1,10 @@ +use rnex_core::kerberos::KerberosDateTime; +use rnex_core::rmc::structures; +use rnex_core::rmc::structures::{Result, RmcSerialize}; use std::io::{Read, Write}; -use crate::kerberos::KerberosDateTime; -use crate::rmc::structures; -use crate::rmc::structures::RmcSerialize; #[derive(Debug, Clone, Default)] -pub enum Variant{ +pub enum Variant { #[default] None, SInt64(i64), @@ -15,9 +15,9 @@ pub enum Variant{ UInt64(u64), } -impl RmcSerialize for Variant{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - match self{ +impl RmcSerialize for Variant { + fn serialize(&self, writer: &mut impl Write) -> Result<()> { + match self { Variant::None => { writer.write_all(&[0])?; } @@ -50,8 +50,8 @@ impl RmcSerialize for Variant{ Ok(()) } - fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { - match u8::deserialize(reader)?{ + fn deserialize(reader: &mut impl Read) -> Result { + match u8::deserialize(reader)? { 0 => Ok(Variant::None), 1 => Ok(Variant::SInt64(i64::deserialize(reader)?)), 2 => Ok(Variant::Double(f64::deserialize(reader)?)), @@ -59,7 +59,7 @@ impl RmcSerialize for Variant{ 4 => Ok(Variant::String(String::deserialize(reader)?)), 5 => Ok(Variant::DateTime(KerberosDateTime::deserialize(reader)?)), 6 => Ok(Variant::UInt64(u64::deserialize(reader)?)), - v => Err(structures::Error::UnexpectedValue(v as u64)) + v => Err(structures::Error::UnexpectedValue(v as u64)), } } -} \ No newline at end of file +} diff --git a/rnex-core/src/rnex_proxy_common.rs b/rnex-core/src/rnex_proxy_common.rs new file mode 100644 index 0000000..d103e43 --- /dev/null +++ b/rnex-core/src/rnex_proxy_common.rs @@ -0,0 +1,40 @@ +use crate::{PID, prudp::socket_addr::PRUDPSockAddr}; +use macros::RmcSerialize; + +#[derive(Debug, RmcSerialize, PartialEq, Eq)] +#[rmc_struct(0)] +pub struct ConnectionInitData { + pub prudpsock_addr: PRUDPSockAddr, + pub pid: PID, +} + +#[cfg(test)] +mod test { + use std::{ + io::Cursor, + net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}, + }; + + use crate::{ + prudp::{socket_addr::PRUDPSockAddr, virtual_port::VirtualPort}, + rmc::structures::RmcSerialize, + rnex_proxy_common::ConnectionInitData, + }; + + #[test] + fn test() { + let data = ConnectionInitData { + prudpsock_addr: PRUDPSockAddr { + regular_socket_addr: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::BROADCAST, 19293)), + virtual_port: VirtualPort::new(10, 2), + }, + pid: 100, + }; + + let ser = data.to_data().unwrap(); + + let de = ConnectionInitData::deserialize(&mut Cursor::new(ser)).unwrap(); + + assert_eq!(data, de); + } +} diff --git a/src/util.rs b/rnex-core/src/util.rs similarity index 94% rename from src/util.rs rename to rnex-core/src/util.rs index 04c8400..ff0150c 100644 --- a/src/util.rs +++ b/rnex-core/src/util.rs @@ -5,7 +5,7 @@ use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt}; use tokio::sync::mpsc::{channel, Receiver, Sender}; use tokio::sync::Notify; use tokio::task; -use rust_nex::reggie::{UnitPacketRead, UnitPacketWrite}; +use crate::reggie::{UnitPacketRead, UnitPacketWrite}; #[derive(Clone)] pub struct SendingBufferConnection(Sender>, Arc); @@ -81,7 +81,9 @@ impl SplittableBufferConnection { } } } - stream.shutdown().await; + if let Err(e) = stream.shutdown().await{ + error!("failed to shut down stream: {}", e); + } }); } diff --git a/src/versions.rs b/rnex-core/src/versions.rs similarity index 100% rename from src/versions.rs rename to rnex-core/src/versions.rs diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..3ac2963 --- /dev/null +++ b/shell.nix @@ -0,0 +1,18 @@ +{ + pkgs ? import { }, + pkg ? pkgs.callPackage ./. { }, +}: +pkgs.mkShell { + # Get dependencies from the main package + # inputsFrom = [ pkg ]; + # Additional tooling + buildInputs = with pkgs; [ + cargo + rust-analyzer # LSP Server + rustfmt # Formatter + clippy # Linter + podman + yq + jq + ]; +} diff --git a/src/endianness.rs b/src/endianness.rs deleted file mode 100644 index 160b1a1..0000000 --- a/src/endianness.rs +++ /dev/null @@ -1,230 +0,0 @@ -use std::{io, mem}; -use std::io::Read; -use std::marker::PhantomData; -use bytemuck::Pod; - -#[cfg(target_endian = "little")] -pub const IS_LITTLE_ENDIAN: bool = true; - -#[cfg(target_endian = "big")] -pub const IS_LITTLE_ENDIAN: bool = false; - -pub const IS_BIG_ENDIAN: bool = !IS_LITTLE_ENDIAN; - -pub mod little_endian{ - use std::io; - use std::io::Read; - - #[inline] - pub fn read_u16(reader: &mut (impl Read + ?Sized)) -> io::Result{ - let mut data = [0u8; 2]; - - reader.read_exact(&mut data)?; - - Ok(((data[0] as u16) << 8) | (data[1] as u16)) - } - - #[inline] - pub fn read_u32(reader: &mut (impl Read + ?Sized)) -> io::Result{ - let mut data = [0u8; 4]; - - reader.read_exact(&mut data)?; - - Ok( - ((data[0] as u32) << 24) | - ((data[1] as u32) << 16) | - ((data[2] as u32) << 8) | - (data[3] as u32) - ) - } -} - -pub struct StructMultiReadIter<'a, T: Pod + SwapEndian>{ - reader: &'a mut dyn Read, - left_to_read: usize, - swap_endian: bool, - _phantom_data: PhantomData<&'static T> -} - -impl<'a, T: Pod + SwapEndian> Iterator for StructMultiReadIter<'a, T>{ - type Item = io::Result; - - #[inline] - fn next(&mut self) -> Option { - if self.left_to_read == 0{ - None - } else { - Some(self.reader.read_struct(self.swap_endian)) - } - } -} - -impl<'a, T: Pod + SwapEndian> Drop for StructMultiReadIter<'a, T>{ - #[inline] - fn drop(&mut self) { - - // read all the structs we would be reading and discard them to make the result after using - // this always be the same - while let Some(_) = self.next() { } - } -} - - - - -pub trait ReadExtensions: Read{ - #[inline] - fn read_le_u16(&mut self) -> io::Result{ - little_endian::read_u16(self) - } - - #[inline] - fn read_le_u32(&mut self) -> io::Result{ - little_endian::read_u32(self) - } - - #[inline] - fn read_le_struct(&mut self) -> io::Result{ - let mut data = T::zeroed(); - let bytes = bytemuck::bytes_of_mut(&mut data); - - self.read_exact(bytes)?; - - if cfg!(not(target_endian = "little")){ - data = data.swap_endian(); - } - - Ok(data) - } - - #[inline] - fn read_struct(&mut self, swap_endian: bool) -> io::Result{ - let mut data = T::zeroed(); - let bytes = bytemuck::bytes_of_mut(&mut data); - - self.read_exact(bytes)?; - - if swap_endian{ - data = data.swap_endian(); - } - - Ok(data) - } - - - fn read_struct_multi(&mut self, swap_endian: bool, count: usize) -> io::Result>; -} - -impl ReadExtensions for T{ - // i was forced to put this here because it requires info about self - #[inline] - fn read_struct_multi(&mut self, swap_endian: bool, count: usize) -> io::Result>{ - Ok(StructMultiReadIter{ - reader: self, - swap_endian, - left_to_read: count, - _phantom_data: Default::default() - }) - } -} - - - - -pub trait SwapEndian: Clone + Copy{ - fn swap_endian(self) -> Self; -} - -impl SwapEndian for u8{ - #[inline] - fn swap_endian(self) -> Self { - self - } -} - -impl SwapEndian for u16{ - #[inline] - fn swap_endian(self) -> Self { - self.swap_bytes() - } -} -impl SwapEndian for u32{ - #[inline] - fn swap_endian(self) -> Self { - self.swap_bytes() - } -} - -impl SwapEndian for u64{ - #[inline] - fn swap_endian(self) -> Self { - self.swap_bytes() - } -} - -impl SwapEndian for f64{ - #[inline] - fn swap_endian(self) -> Self { - //trust me this is safe - unsafe{ mem::transmute::<_, f64>(mem::transmute::<_, u64>(self).swap_bytes()) } - } -} - -impl SwapEndian for i8{ - #[inline] - fn swap_endian(self) -> Self { - self - } -} - -impl SwapEndian for i16{ - #[inline] - fn swap_endian(self) -> Self { - self.swap_bytes() - } -} -impl SwapEndian for i32{ - #[inline] - fn swap_endian(self) -> Self { - self.swap_bytes() - } -} - -impl SwapEndian for i64{ - #[inline] - fn swap_endian(self) -> Self { - self.swap_bytes() - } -} - -impl SwapEndian for (T, U){ - #[inline] - fn swap_endian(self) -> Self { - (self.0.swap_endian(), self.1.swap_endian()) - } -} - -impl SwapEndian for (T, U, V){ - #[inline] - fn swap_endian(self) -> Self { - (self.0.swap_endian(), self.1.swap_endian(), self.2.swap_endian()) - } -} - -impl SwapEndian for (T, U, V, W){ - #[inline] - fn swap_endian(self) -> Self { - (self.0.swap_endian(), self.1.swap_endian(), self.2.swap_endian(), self.3.swap_endian()) - } -} - -impl SwapEndian for [T; SIZE]{ - #[inline] - fn swap_endian(mut self) -> Self { - for elem in &mut self{ - *elem = elem.swap_endian(); - } - - self - } -} \ No newline at end of file diff --git a/src/executables/backend_server_insecure.rs b/src/executables/backend_server_insecure.rs deleted file mode 100644 index 4cb8634..0000000 --- a/src/executables/backend_server_insecure.rs +++ /dev/null @@ -1,97 +0,0 @@ -use rust_nex::reggie::{RemoteEdgeNodeHolder, UnitPacketRead}; -use log::{error, info}; -use once_cell::sync::Lazy; -use rustls::client::danger::HandshakeSignatureValid; -use rustls::pki_types::{CertificateDer, TrustAnchor, UnixTime}; -use rustls::server::danger::{ClientCertVerified, ClientCertVerifier}; -use rustls::server::{ClientCertVerifierBuilder, WebPkiClientVerifier}; -use rustls::{ - DigitallySignedStruct, DistinguishedName, Error, RootCertStore, ServerConfig, ServerConnection, - SignatureScheme, -}; -use rustls_pki_types::PrivateKeyDer; -use rust_nex::common::setup; -use std::borrow::ToOwned; -use std::{env, fs}; -use std::io::Cursor; -use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; -use std::sync::Arc; -use macros::{method_id, rmc_proto, rmc_struct}; -use tokio::io::AsyncReadExt; -use tokio::net::{TcpListener, TcpSocket, TcpStream}; -use tokio::task; -use tokio_rustls::TlsAcceptor; -use rust_nex::define_rmc_proto; -use rust_nex::executables::common::{OWN_IP_PRIVATE, SECURE_EDGE_NODE_HOLDER, SECURE_SERVER_ACCOUNT, SERVER_PORT}; -use rust_nex::nex::auth_handler::AuthHandler; -use rust_nex::reggie::EdgeNodeHolderConnectOption::DontRegister; -use rust_nex::rmc::protocols::{new_rmc_gateway_connection, OnlyRemote}; -use rust_nex::rmc::response::ErrorCode; -use rust_nex::rmc::structures::RmcSerialize; -use rust_nex::rnex_proxy_common::ConnectionInitData; -use rust_nex::util::SplittableBufferConnection; - -pub static SECURE_PROXY_ADDR: Lazy = Lazy::new(|| { - env::var("SECURE_PROXY_ADDR") - .ok() - .and_then(|s| s.parse().ok()) - .expect("no secure proxy ip specified") -}); - -pub static SECURE_PROXY_PORT: Lazy = Lazy::new(|| { - env::var("SECURE_PROXY_PORT") - .ok() - .and_then(|s| s.parse().ok()) - .unwrap_or(10000) -}); - - - -#[tokio::main] -async fn main() { - setup(); - - let conn = TcpStream::connect(&*SECURE_EDGE_NODE_HOLDER).await.unwrap(); - - let conn: SplittableBufferConnection = conn.into(); - - conn.send(DontRegister.to_data()).await; - - let conn = new_rmc_gateway_connection(conn, |r| Arc::new(OnlyRemote::::new(r))); - - 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 controller = conn.clone(); - task::spawn(async move { - info!("connection to secure backend established"); - new_rmc_gateway_connection(stream.into(), |_| { - Arc::new(AuthHandler { - destination_server_acct: &SECURE_SERVER_ACCOUNT, - build_name: "branch:origin/project/wup-agmj build:3_8_15_2004_0", - control_server: controller - }) - }); - }); - - } -} diff --git a/src/executables/backend_server_secure.rs b/src/executables/backend_server_secure.rs deleted file mode 100644 index 6ada7b8..0000000 --- a/src/executables/backend_server_secure.rs +++ /dev/null @@ -1,74 +0,0 @@ -use std::io::Cursor; -use rust_nex::rmc::structures::RmcSerialize; -use rust_nex::reggie::{RemoteEdgeNodeHolder, UnitPacketRead}; -use std::net::SocketAddrV4; -use std::sync::Arc; -use std::sync::atomic::AtomicU32; -use log::{error, info}; -use tokio::net::{TcpListener, TcpStream}; -use tokio::task; -use rust_nex::common::setup; -use rust_nex::executables::common::{OWN_IP_PRIVATE, SECURE_EDGE_NODE_HOLDER, SERVER_PORT}; -use rust_nex::nex::matchmake::MatchmakeManager; -use rust_nex::nex::remote_console::RemoteConsole; -use rust_nex::nex::user::User; -use rust_nex::reggie::EdgeNodeHolderConnectOption::DontRegister; -use rust_nex::rmc::protocols::{new_rmc_gateway_connection, OnlyRemote}; -use rust_nex::rnex_proxy_common::ConnectionInitData; -use rust_nex::rmc::protocols::RemoteInstantiatable; -use rust_nex::util::SplittableBufferConnection; - -#[tokio::main] -async fn main() { - setup(); - - let listen = TcpListener::bind(SocketAddrV4::new(*OWN_IP_PRIVATE, *SERVER_PORT)).await.unwrap(); - - 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; - - 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 mmm = mmm.clone(); - task::spawn(async move { - info!("connection to secure backend established"); - new_rmc_gateway_connection(stream.into(), |r| { - Arc::new_cyclic(|this| User{ - this: this.clone(), - ip: user_connection_data.prudpsock_addr, - pid: user_connection_data.pid, - remote: RemoteConsole::new(r), - matchmake_manager: mmm, - station_url: Default::default() - }) - }); - }); - - } -} \ No newline at end of file diff --git a/src/executables/common.rs b/src/executables/common.rs deleted file mode 100644 index 3114436..0000000 --- a/src/executables/common.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::env; -use std::net::{Ipv4Addr, SocketAddrV4}; -use macros::{method_id, rmc_proto, RmcSerialize}; -use once_cell::sync::Lazy; -use tonic::transport::Server; -use rust_nex::define_rmc_proto; -use rust_nex::prudp::station_url::StationUrl; -use crate::nex::account::Account; -use crate::rmc::response::ErrorCode; - -pub static OWN_IP_PRIVATE: Lazy = Lazy::new(|| { - env::var("SERVER_IP") - .ok() - .and_then(|s| s.parse().ok()) - .expect("SERVER_IP not specified") -}); - -pub static OWN_IP_PUBLIC: Lazy = Lazy::new(|| { - env::var("SERVER_IP_PUBLIC") - .ok() - .and_then(|s| s.parse().ok()) - .expect("SERVER_IP_PUBLIC not specified") -}); - -pub static SERVER_PORT: Lazy = Lazy::new(|| { - env::var("SERVER_PORT") - .ok() - .and_then(|s| s.parse().ok()) - .unwrap_or(10000) -}); - -pub static KERBEROS_SERVER_PASSWORD: Lazy = Lazy::new(|| { - env::var("AUTH_SERVER_PASSWORD") - .ok() - .unwrap_or("password".to_owned()) -}); - -pub static AUTH_SERVER_ACCOUNT: Lazy = - Lazy::new(|| Account::new(1, "Quazal Authentication", &KERBEROS_SERVER_PASSWORD)); -pub static SECURE_SERVER_ACCOUNT: Lazy = - Lazy::new(|| Account::new(2, "Quazal Rendez-Vous", &KERBEROS_SERVER_PASSWORD)); - -pub static SECURE_EDGE_NODE_HOLDER: Lazy = Lazy::new(||{ - env::var("SECURE_EDGE_NODE_HOLDER") - .ok() - .and_then(|s| s.parse().ok()) - .expect("SECURE_EDGE_NODE_HOLDER not set") -}); - -pub static FORWARD_DESTINATION: Lazy = - Lazy::new(|| - env::var("FORWARD_DESTINATION") - .ok() - .and_then(|s| s.parse().ok()) - .expect("FORWARD_DESTINATION not set") - ); diff --git a/src/executables/mod.rs b/src/executables/mod.rs deleted file mode 100644 index ffac467..0000000 --- a/src/executables/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod common; \ No newline at end of file diff --git a/src/executables/proxy_insecure.rs b/src/executables/proxy_insecure.rs deleted file mode 100644 index 1232dc3..0000000 --- a/src/executables/proxy_insecure.rs +++ /dev/null @@ -1,123 +0,0 @@ - -use rust_nex::reggie::RemoteEdgeNodeHolder; -use std::env; -use std::ffi::CStr; -use std::io::{Read, Write}; -use std::net::{Ipv4Addr, SocketAddrV4}; -use std::sync::{Arc, OnceLock}; -use std::time::Duration; -use bytemuck::{Pod, Zeroable}; -use chacha20::{ChaCha20, Key}; -use chacha20::cipher::{Iv, KeyIvInit, StreamCipher}; -use log::{error, warn}; -use macros::rmc_struct; -use once_cell::sync::Lazy; -use rsa::pkcs8::{DecodePrivateKey, DecodePublicKey, Document}; -use rsa::{BigUint, Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey}; -use rsa::pkcs1::EncodeRsaPublicKey; -use rsa::pss::BlindedSigningKey; -use rsa::signature::{RandomizedSigner, SignatureEncoding}; -use sha2::Sha256; -use tokio::net::{TcpSocket, TcpStream}; -use tokio::sync::RwLock; -use tokio::task; -use tokio::time::sleep; -use rust_nex::common::setup; -use rust_nex::executables::common::{FORWARD_DESTINATION, OWN_IP_PRIVATE, OWN_IP_PUBLIC, SECURE_EDGE_NODE_HOLDER, SERVER_PORT}; -use rust_nex::prudp::packet::VirtualPort; -use rust_nex::prudp::router::Router; -use rust_nex::prudp::station_url::StationUrl; -use rust_nex::prudp::unsecure::Unsecure; -use rust_nex::reggie::{UnitPacketRead, UnitPacketWrite}; -use rust_nex::reggie::EdgeNodeHolderConnectOption::{DontRegister, Register}; -use rust_nex::rmc::protocols::{new_rmc_gateway_connection, OnlyRemote}; -use rust_nex::rmc::response::ErrorCode; -use rust_nex::rmc::structures::RmcSerialize; -use rust_nex::rnex_proxy_common::ConnectionInitData; -use rust_nex::util::SplittableBufferConnection; - - - - -#[tokio::main] -async fn main() { - setup(); - - let conn = tokio::net::TcpStream::connect(&*SECURE_EDGE_NODE_HOLDER).await.unwrap(); - - let conn: SplittableBufferConnection = conn.into(); - - conn.send(Register(SocketAddrV4::new(*OWN_IP_PUBLIC, *SERVER_PORT)).to_data()).await; - - let conn = new_rmc_gateway_connection(conn, |r| Arc::new(OnlyRemote::::new(r))); - - let (router_secure, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *SERVER_PORT)) - .await - .expect("unable to start router"); - - let mut socket_secure = router_secure - .add_socket(VirtualPort::new(1, 10), Unsecure( - "6f599f81" - )) - .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()).await{ - error!("error connecting to backend: {}", e); - return; - }; - - loop { - tokio::select! { - data = conn.recv() => { - let Some(data) = data else { - break; - }; - - if let Err(e) = stream.send_buffer(&data[..]).await{ - error!("error sending data to backend: {}", e); - break; - } - }, - data = stream.read_buffer() => { - let data = match data{ - Ok(d) => d, - Err(e) => { - error!("error reveiving data from backend: {}", e); - break; - } - }; - - if conn.send(data).await == None{ - return; - } - }, - _ = sleep(Duration::from_secs(10)) => { - conn.send([0,0,0,0,0].to_vec()).await; - } - } - } - }); - } -} \ No newline at end of file diff --git a/src/grpc/protobufs.rs b/src/grpc/protobufs.rs deleted file mode 100644 index bae7331..0000000 --- a/src/grpc/protobufs.rs +++ /dev/null @@ -1,5 +0,0 @@ - -pub mod account { - tonic::include_proto!("account"); -} - diff --git a/src/kerberos/mod.rs b/src/kerberos/mod.rs deleted file mode 100644 index a40b816..0000000 --- a/src/kerberos/mod.rs +++ /dev/null @@ -1,170 +0,0 @@ -use std::io::{Read, Write}; -use bytemuck::{bytes_of, Pod, Zeroable}; -use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc}; -use hmac::Hmac; -use md5::{Digest, Md5}; -use rc4::{Rc4, Rc4Core, StreamCipher}; -use rc4::cipher::StreamCipherCoreWrapper; -use rc4::consts::U16; -use hmac::Mac; -use rc4::KeyInit; -use crate::rmc::structures::RmcSerialize; - -type Md5Hmac = Hmac; - -pub fn derive_key(pid: u32, password: [u8; 16]) -> [u8; 16]{ - let iteration_count = 65000 + pid%1024; - - let mut key = password; - - for _ in 0..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{ - 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 dyn Write) -> crate::rmc::structures::Result<()> { - Ok(self.0.serialize(writer)?) - } - - fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { - Ok(Self(u64::deserialize(reader)?)) - } -} - -#[derive(Pod, Zeroable, Copy, Clone)] -#[repr(C, packed)] -pub struct TicketInternalData{ - pub issued_time: KerberosDateTime, - pub pid: u32, - pub session_key: [u8; 32], -} - -impl TicketInternalData{ - pub(crate) fn new(pid: u32) -> 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> = Rc4::new_from_slice(&key).unwrap(); - rc4.apply_keystream(&mut data); - - let mut hmac = ::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, Copy, Clone)] -#[repr(C, packed)] -pub struct Ticket{ - pub session_key: [u8; 32], - pub pid: u32, -} - -impl Ticket{ - pub(crate) 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> = Rc4::new_from_slice(&key).unwrap(); - rc4.apply_keystream(&mut data); - - let mut hmac = ::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 chrono::{Datelike, Utc}; - use crate::kerberos::KerberosDateTime; - - #[test] - fn kerberos_time_convert_test(){ - let time = KerberosDateTime(135904948834); - - println!("{}", time.to_regular_time().to_rfc2822()); - } -} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 338b9d3..0000000 --- a/src/main.rs +++ /dev/null @@ -1,425 +0,0 @@ -#![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 crate::nex::account::Account; -use crate::nex::auth_handler::{AuthHandler, RemoteAuthClientProtocol}; -use crate::nex::remote_console::RemoteConsole; -use crate::nex::user::{RemoteUserProtocol, User}; -use crate::prudp::packet::VirtualPort; -use crate::prudp::router::Router; -use crate::prudp::secure::Secure; -use crate::prudp::sockaddr::PRUDPSockAddr; -use crate::prudp::unsecure::Unsecure; -use crate::rmc::protocols::auth::Auth; -use crate::rmc::protocols::auth::RawAuth; -use crate::rmc::protocols::auth::RawAuthInfo; -use crate::rmc::protocols::auth::RemoteAuth; -use crate::rmc::protocols::matchmake_extension::RemoteMatchmakeExtension; -use crate::rmc::protocols::{new_rmc_gateway_connection, OnlyRemote, RemoteInstantiatable}; -use crate::rmc::response::ErrorCode; -use crate::rmc::structures::any::Any; -use crate::rmc::structures::connection_data::ConnectionData; -use crate::rmc::structures::matchmake::{CreateMatchmakeSessionParam, Gathering, MatchmakeParam, MatchmakeSession}; -use crate::rmc::structures::qresult::QResult; -use chrono::{Local, SecondsFormat}; -use log::{error, info}; -use macros::rmc_struct; -use once_cell::sync::Lazy; -use simplelog::{ - ColorChoice, CombinedLogger, Config, LevelFilter, TermLogger, TerminalMode, WriteLogger, -}; -use std::fs::File; -use std::marker::PhantomData; -use std::net::{Ipv4Addr, SocketAddrV4}; -use std::ops::{BitAnd, BitOr}; -use std::str::FromStr; -use std::sync::{Arc, Once, Weak}; -use std::time::Duration; -use std::{env, fs}; -use std::sync::atomic::AtomicU32; -use tokio::task::JoinHandle; -use crate::kerberos::KerberosDateTime; -use crate::nex::matchmake::MatchmakeManager; -use crate::rmc::protocols::secure::RemoteSecure; - -mod endianness; -mod prudp; -pub mod rmc; -//mod protocols; - -mod grpc; -mod kerberos; -mod nex; -mod result; -mod versions; -mod web; -pub mod reggie; -pub mod util; -pub mod common; - - - -static KERBEROS_SERVER_PASSWORD: Lazy = Lazy::new(|| { - env::var("AUTH_SERVER_PASSWORD") - .ok() - .unwrap_or("password".to_owned()) -}); - -static AUTH_SERVER_ACCOUNT: Lazy = - Lazy::new(|| Account::new(1, "Quazal Authentication", &KERBEROS_SERVER_PASSWORD)); -static SECURE_SERVER_ACCOUNT: Lazy = - Lazy::new(|| Account::new(2, "Quazal Rendez-Vous", &KERBEROS_SERVER_PASSWORD)); - -static AUTH_SERVER_PORT: Lazy = Lazy::new(|| { - env::var("AUTH_SERVER_PORT") - .ok() - .and_then(|s| s.parse().ok()) - .unwrap_or(10000) -}); -static SECURE_SERVER_PORT: Lazy = Lazy::new(|| { - env::var("SECURE_SERVER_PORT") - .ok() - .and_then(|s| s.parse().ok()) - .unwrap_or(10001) -}); - -static OWN_IP_PRIVATE: Lazy = Lazy::new(|| { - env::var("SERVER_IP") - .ok() - .and_then(|s| s.parse().ok()) - .expect("no private ip specified") -}); - -static OWN_IP_PUBLIC: Lazy = - Lazy::new(|| env::var("SERVER_IP_PUBLIC").unwrap_or(OWN_IP_PRIVATE.to_string())); - -static SECURE_STATION_URL: Lazy = Lazy::new(|| { - format!( - "prudps:/PID=2;sid=1;stream=10;type=2;address={};port={};CID=1", - *OWN_IP_PUBLIC, *SECURE_SERVER_PORT - ) -}); - -static FORCE_EXIT: Once = Once::new(); - -#[tokio::main] -async fn main() { - CombinedLogger::init(vec![ - TermLogger::new( - LevelFilter::Info, - Config::default(), - TerminalMode::Mixed, - ColorChoice::Auto, - ), - WriteLogger::new(LevelFilter::max(), Config::default(), { - fs::create_dir_all("log").unwrap(); - let date = Local::now().to_rfc3339_opts(SecondsFormat::Secs, false); - // this fixes windows being windows - let date = date.replace(":", "-"); - let filename = format!("{}.log", date); - if cfg!(windows) { - File::create(format!("log\\{}", filename)).unwrap() - } else { - File::create(format!("log/{}", filename)).unwrap() - } - }), - ]) - .unwrap(); - - ctrlc::set_handler(||{ - FORCE_EXIT.call_once_force(|_|{ - println!("attempting exit"); - }); - }).unwrap(); - - dotenv::dotenv().ok(); - - //start_servers().await; -} -/* - -struct AuthServer{ - router: Arc, - join_handle: JoinHandle<()>, - socket: Socket -} - -async fn start_auth_server() -> AuthServer{ - info!("starting auth server on {}:{}", *OWN_IP_PRIVATE, *AUTH_SERVER_PORT); - - let (router, join_handle) = - Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *AUTH_SERVER_PORT)).await - .expect("unable to startauth server"); - - info!("setting up endpoints"); - - // dont assign it to the name _ as that will make it drop right here and now - - let auth_protocol_config = AuthProtocolConfig{ - secure_server_account: &SECURE_SERVER_ACCOUNT, - build_name: "branch:origin/project/wup-agmj build:3_8_15_2004_0", - station_url: &SECURE_STATION_URL - }; - - let rmcserver = RMCProtocolServer::new(Box::new([ - Box::new(auth::bound_protocol(auth_protocol_config)) - ])); - - let socket = - Socket::new( - router.clone(), - VirtualPort::new(1,10), - "6f599f81", - Box::new(|_, count|{ - Box::pin( - async move { - - - let encryption_pairs = Vec::from_iter((0..=count).map(|_v| { - let rc4: Rc4 = Rc4::new_from_slice( "CD&ML".as_bytes()).unwrap(); - let cypher = Box::new(rc4); - let server_cypher: Box = cypher; - - let rc4: Rc4 = Rc4::new_from_slice( "CD&ML".as_bytes()).unwrap(); - let cypher = Box::new(rc4); - let client_cypher: Box = cypher; - - EncryptionPair{ - recv: client_cypher, - send: server_cypher - } - })); - - Some((Vec::new(), encryption_pairs, None)) - } - ) - }), - Box::new(move |packet, socket, connection|{ - let rmcserver = rmcserver.clone(); - Box::pin(async move { rmcserver.process_message(packet, socket, connection).await; }) - }) - ).await.expect("unable to create socket"); - - AuthServer{ - join_handle, - router, - socket, - } -} - -struct SecureServer{ - router: Arc, - join_handle: JoinHandle<()>, - socket: Socket -} - -async fn start_secure_server() -> SecureServer{ - info!("starting secure server on {}:{}", *OWN_IP_PRIVATE, *SECURE_SERVER_PORT); - - let (router, join_handle) = - Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *SECURE_SERVER_PORT)).await - .expect("unable to startauth server"); - - info!("setting up endpoints"); - - let matchmake_data = Arc::new(RwLock::new( - MatchmakeData{ - matchmake_sessions: BTreeMap::new() - } - )); - - let rmcserver = RMCProtocolServer::new(Box::new([ - Box::new(block_if_maintenance), - Box::new(protocols::secure::bound_protocol()), - Box::new(protocols::matchmake::bound_protocol(matchmake_data.clone())), - Box::new(protocols::matchmake_extension::bound_protocol(matchmake_data)), - Box::new(protocols::nat_traversal::bound_protocol()) - ])); - - let socket = - Socket::new( - router.clone(), - VirtualPort::new(1,10), - "6f599f81", - Box::new(|p, count|{ - Box::pin( - async move { - let (session_key, pid, check_value) = read_secure_connection_data(&p.payload, &SECURE_SERVER_ACCOUNT)?; - - let check_value_response = check_value + 1; - - let data = bytemuck::bytes_of(&check_value_response); - - let mut response = Vec::new(); - - data.serialize(&mut response).ok()?; - - let encryption_pairs = generate_secure_encryption_pairs(session_key, count); - - Some((response, encryption_pairs, Some( - ActiveSecureConnectionData{ - pid, - session_key - } - ))) - } - ) - }), - Box::new(move |packet, socket, connection|{ - let rmcserver = rmcserver.clone(); - Box::pin(async move { rmcserver.process_message(packet, socket, connection).await; }) - }) - ).await.expect("unable to create socket"); - - SecureServer{ - join_handle, - router, - socket, - } -}*/ -/* -async fn start_auth() -> JoinHandle<()> { - tokio::spawn(async { - let (router_secure, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *AUTH_SERVER_PORT)) - .await - .expect("unable to start router"); - - let mut socket_secure = router_secure - .add_socket(VirtualPort::new(1, 10), Unsecure( - "6f599f81" - )) - .await - .expect("unable to add socket"); - - // let conn = socket_secure.connect(auth_sockaddr).await.unwrap(); - - while !FORCE_EXIT.is_completed() { - let Some(conn) = socket_secure.accept().await else { - error!("server crashed"); - return; - }; - - info!("new connected user!"); - - let _ = new_rmc_gateway_connection(conn, |_| { - Arc::new(AuthHandler { - destination_server_acct: &SECURE_SERVER_ACCOUNT, - build_name: "branch:origin/project/wup-agmj build:3_8_15_2004_0", - station_url: &SECURE_STATION_URL, - }) - }); - } - }) -} - -async fn start_secure() -> JoinHandle<()> { - tokio::spawn(async { - 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; - - let web_server = web::start_web(mmm.clone()).await; - - let (router_secure, _) = - Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *SECURE_SERVER_PORT)) - .await - .expect("unable to start router"); - - let mut socket_secure = router_secure - .add_socket( - VirtualPort::new(1, 10), - Secure( - "6f599f81", - &SECURE_SERVER_ACCOUNT - ), - ) - .await - .expect("unable to add socket"); - - // let conn = socket_secure.connect(auth_sockaddr).await.unwrap(); - - while !FORCE_EXIT.is_completed() { - let Some(conn) = socket_secure.accept().await else { - error!("server crashed"); - return; - }; - - info!("new connected user on secure :D!"); - - let ip = conn.socket_addr; - let pid = conn.user_id; - - let _ = new_rmc_gateway_connection(conn, |r| { - Arc::new_cyclic(|w| User { - ip, - pid, - this: w.clone(), - remote: RemoteConsole::new(r), - station_url: Default::default(), - matchmake_manager: mmm.clone() - }) - }); - } - }) -} - -async fn start_test() { - let addr = SocketAddrV4::new(*OWN_IP_PRIVATE, *SECURE_SERVER_PORT); - - let virt_addr = VirtualPort::new(1, 10); - let prudp_addr = PRUDPSockAddr::new(addr, virt_addr); - - let (router_test, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, 26969)) - .await - .expect("unable to start router"); - - let mut socket_secure = router_test - .add_socket(VirtualPort::new(1, 10), Unsecure("6f599f81")) - .await - .expect("unable to add socket"); - - let conn = socket_secure.connect(prudp_addr).await.unwrap(); - - let remote = new_rmc_gateway_connection(conn, |r| { - Arc::new(OnlyRemote::::new(r)) - }); - - tokio::time::sleep(Duration::from_secs(1)).await; - let urls = vec!["prudp:/address=192.168.178.45;port=60146;Pl=2;natf=0;natm=0;pmp=0;sid=15;upnp=0".to_owned()]; -} - -async fn start_servers() { - #[cfg(feature = "auth")] - let auth_server = start_auth().await; - #[cfg(feature = "secure")] - let secure_server = start_secure().await; - - - tokio::time::sleep(Duration::from_secs(1)).await; - - //start_test().await; - - - - #[cfg(feature = "auth")] - auth_server.await.expect("auth server crashed"); - #[cfg(feature = "secure")] - secure_server.await.expect("auth server crashed"); -} -*/ \ No newline at end of file diff --git a/src/nex/account.rs b/src/nex/account.rs deleted file mode 100644 index 75c53ce..0000000 --- a/src/nex/account.rs +++ /dev/null @@ -1,39 +0,0 @@ -use macros::RmcSerialize; - -#[derive(RmcSerialize)] -#[derive(Clone)] -pub struct Account{ - pub pid: u32, - pub username: String, - pub kerbros_password: [u8; 16], -} - -impl Account{ - pub fn new(pid: u32, username: &str, passwd: &str) -> Self{ - let passwd_data = passwd.as_bytes(); - - let mut passwd = [0u8; 16]; - - for (idx, byte) in passwd_data.iter().enumerate(){ - passwd[idx] = *byte; - } - - Self{ - kerbros_password: passwd, - username: username.into(), - pid - } - } - - pub fn new_raw_password(pid: u32, username: &str, passwd: [u8; 16]) -> Self{ - Self{ - kerbros_password: passwd, - username: username.into(), - pid - } - } - - pub fn get_login_data(&self) -> (u32, [u8; 16]){ - (self.pid, self.kerbros_password) - } -} \ No newline at end of file diff --git a/src/nex/auth_handler.rs b/src/nex/auth_handler.rs deleted file mode 100644 index ca7f970..0000000 --- a/src/nex/auth_handler.rs +++ /dev/null @@ -1,187 +0,0 @@ -use std::hash::{DefaultHasher, Hasher}; -use std::net::SocketAddrV4; -use std::sync::Arc; -use crate::grpc::account; -use crate::kerberos::{derive_key, KerberosDateTime, Ticket}; -use crate::nex::account::Account; -use crate::rmc::protocols::auth::{Auth, RawAuth, RawAuthInfo, RemoteAuth}; -use crate::rmc::response::ErrorCode; -use crate::rmc::response::ErrorCode::Core_Unknown; -use crate::rmc::structures::any::Any; -use crate::rmc::structures::connection_data::ConnectionData; -use crate::rmc::structures::qresult::QResult; -use crate::{define_rmc_proto, kerberos}; -use macros::rmc_struct; -use crate::reggie::{RemoteEdgeNodeHolder, RemoteEdgeNodeManagement}; -use crate::rmc::protocols::OnlyRemote; - -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>, -} - -pub fn generate_ticket( - source_act_login_data: (u32, [u8; 16]), - dest_act_login_data: (u32, [u8; 16]), -) -> Box<[u8]> { - let source_key = derive_key(source_act_login_data.0, source_act_login_data.1); - let dest_key = derive_key(dest_act_login_data.0, dest_act_login_data.1); - - let internal_data = kerberos::TicketInternalData::new(source_act_login_data.0); - - let encrypted_inner = internal_data.encrypt(dest_key); - let encrypted_session_ticket = Ticket { - pid: dest_act_login_data.0, - session_key: internal_data.session_key, - } - .encrypt(source_key, &encrypted_inner); - - encrypted_session_ticket -} - -async fn get_login_data_by_pid(pid: u32) -> Option<(u32, [u8; 16])> { - let Ok(mut client) = account::Client::new().await else { - return None; - }; - - let Ok(passwd) = client.get_nex_password(pid).await else { - return None; - }; - - Some((pid, passwd)) -} - -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() - ) -} - -impl Auth for AuthHandler { - async fn login(&self, _name: String) -> Result<(), ErrorCode> { - todo!() - } - - async fn login_ex( - &self, - name: String, - _extra_data: Any, - ) -> Result<(QResult, u32, Vec, ConnectionData, String), ErrorCode> { - let Ok(pid) = name.parse() else { - return Err(ErrorCode::Core_InvalidArgument); - }; - - let Ok(mut client) = account::Client::new().await else { - return Err(ErrorCode::Core_Exception); - }; - - let Ok(passwd) = client.get_nex_password(pid).await else { - return Err(ErrorCode::Core_Exception); - }; - - let source_login_data = (pid, passwd); - let destination_login_data = self.destination_server_acct.get_login_data(); - - let ticket = generate_ticket(source_login_data, destination_login_data); - - let result = QResult::success(Core_Unknown); - - let mut hasher = DefaultHasher::new(); - - hasher.write(name.as_bytes()); - - let Ok(addr) = self.control_server.get_url(hasher.finish()).await else { - 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(), - }; - - Ok(( - result, - source_login_data.0, - ticket.into(), - connection_data, - self.build_name.to_string() //format!("{}; Rust NEX Version {} by DJMrTV", self.build_name, env!("CARGO_PKG_VERSION")), - )) - } - - async fn request_ticket( - &self, - source_pid: u32, - destination_pid: u32, - ) -> Result<(QResult, Vec), ErrorCode> { - let Some(source_login_data) = get_login_data_by_pid(source_pid).await else { - return Err(ErrorCode::Core_Exception); - }; - - let desgination_login_data = if destination_pid == self.destination_server_acct.pid { - self.destination_server_acct.get_login_data() - } else { - let Some(login) = get_login_data_by_pid(destination_pid).await else { - return Err(ErrorCode::Core_Exception); - }; - login - }; - - let result = QResult::success(Core_Unknown); - - let ticket = generate_ticket(source_login_data, desgination_login_data); - - Ok((result, ticket.into())) - } - - async fn get_pid(&self, _username: String) -> Result { - Err(ErrorCode::Core_Exception) - } - - async fn get_name(&self, _pid: u32) -> Result { - Err(ErrorCode::Core_Exception) - } -} - -#[cfg(test)] -mod test { - use crate::rmc::structures::connection_data::ConnectionData; - use crate::rmc::structures::qresult::QResult; - use crate::rmc::structures::RmcSerialize; - use crate::rmc::response::RMCResponse; - use std::io::Cursor; - - #[test] - fn test() { - - let stuff = hex::decode("200100000a0106000000028000000100010051b3995774000000a6321c7f78847c1c5e9fb825eb26bd91841f1a40d92fc694159666119cb13527f1463ac48ad42a63e6613ede67041554b1770978112e6f1f3e177a2bfc75933216dbe38f70133a1eb28e2ae32a4b5c4b0c3e3efd4c02907992e259b257270b57a9dbe7792f4721b07f8fafb9e32d50f2555c616a015c0000004b007072756470733a2f5049443d323b7369643d313b73747265616d3d31303b747970653d323b616464726573733d322e3234332e39352e3131333b706f72743d31303030313b4349443d3100000000000100002c153ba51f00000033006272616e63683a6f726967696e2f70726f6a6563742f7775702d61676d6a206275696c643a335f385f31355f323030345f3000").unwrap(); - let stuff = RMCResponse::new(&mut Cursor::new(stuff)).unwrap(); - - let crate::rmc::response::RMCResponseResult::Success { call_id, method_id, data: stuff} = stuff.response_result else { - panic!() - }; - - - - // let stuff = hex::decode("0100010051B399577400000085F1736FCFBE93660275A3FE36FED6C2EFC57222AC99A9219CF54170A415B02DF1463AC48AD42A6307813FDE67041554B177097832ED000F892D9551A09F88E9CB0388DC1BC9527CC7384556A3287B2A349ABBF7E34A5A3EC14C2287CC7F78DA616BC3B03A035347FBD2E9A505C8EF42447CD809015F0000004E007072756470733A2F73747265616D3D31303B747970653D323B616464726573733D3139322E3136382E3137382E3132303B706F72743D31303030313B4349443D313B5049443D323B7369643D310000000000010000CDF53AA51F00000033006272616E63683A6F726967696E2F70726F6A6563742F7775702D61676D6A206275696C643A335F385F31355F323030345F3000").unwrap(); - // let stuff = hex::decode("0100010051b399577400000037d3d4814d2b16dd546c94a75d32637b45f856b5abe73cf26cfaa235c5f2c1cef1463ac48ad42a637d873fde67041554b177097880cfa7e10bb810eaf686bfb0a0cf3d65b1f476ebc046d0855327986f557dca14fbb8594883c186b863f2206f22baa0309dbcc81da2f883cb2cdc12628ec7fced015c0000004b007072756470733a2f5049443d323b7369643d313b73747265616d3d31303b747970653d323b616464726573733d322e3234332e39352e3131333b706f72743d31303030313b4349443d310000000000010000b7f33aa51f00000033006272616e63683a6f726967696e2f70726f6a6563742f7775702d61676d6a206275696c643a335f385f31355f323030345f3000").unwrap(); - - let data = <(QResult, u32, Vec, ConnectionData, String) as RmcSerialize>::deserialize( - &mut Cursor::new(stuff), - ).unwrap(); - - println!("data: {:?}", data); - } -} diff --git a/src/nex/matchmake.rs b/src/nex/matchmake.rs deleted file mode 100644 index bba55f5..0000000 --- a/src/nex/matchmake.rs +++ /dev/null @@ -1,386 +0,0 @@ -use std::collections::HashMap; -use std::str::FromStr; -use std::sync::{Arc, Weak}; -use std::sync::atomic::AtomicU32; -use std::sync::atomic::Ordering::Relaxed; -use std::time::Duration; -use log::info; -use rand::random; -use tokio::sync::{Mutex, RwLock}; -use tokio::time::sleep; -use crate::kerberos::KerberosDateTime; -use crate::nex::user::User; -use crate::rmc::protocols::notifications::{NotificationEvent, RemoteNotification}; -use crate::rmc::protocols::notifications::notification_types::{HOST_CHANGED, OWNERSHIP_CHANGED}; -use crate::rmc::response::ErrorCode; -use crate::rmc::response::ErrorCode::{Core_InvalidArgument, RendezVous_SessionVoid}; -use crate::rmc::structures::matchmake::{Gathering, MatchmakeParam, MatchmakeSession, MatchmakeSessionSearchCriteria}; -use crate::rmc::structures::matchmake::gathering_flags::PERSISTENT_GATHERING; -use crate::rmc::structures::variant::Variant; - -pub struct MatchmakeManager{ - pub gid_counter: AtomicU32, - pub sessions: RwLock>>>, - pub rv_cid_counter: AtomicU32, - pub users: RwLock>> -} - -impl MatchmakeManager{ - pub fn next_gid(&self) -> u32{ - 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>, 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 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); - } - } - - 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){ - 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>, -} - -fn read_bounds_string(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(compare: T, str: &str) -> Option{ - let bounds: (T, T) = read_bounds_string(str)?; - - Some(bounds.0 <= compare && compare <= bounds.1) -} - -pub async fn broadcast_notification>(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>{ - 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) -> 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], join_msg: String) { - let Some(initiating_user) = conns[0].upgrade() else { - return - }; - - let initiating_pid = initiating_user.pid; - - let old_particip = self.connected_players.clone(); - for conn in conns { - self.connected_players.push(conn.clone()); - } - self.session.participation_count = self.connected_players.len() as u32; - - for other_connection in &conns[1..]{ - let Some(other_conn) = other_connection.upgrade() else { - continue; - }; - - - let other_pid = other_conn.pid; - /*if other_pid == self.session.gathering.owner_pid && - joining_pid == self.session.gathering.owner_pid{ - continue; - }*/ - - other_conn.remote.process_notification_event(NotificationEvent{ - pid_source: initiating_pid, - notif_type: 122000, - param_1: self.session.gathering.self_gid, - param_2: other_pid, - str_param: "".into(), - param_3: 0 - }).await; - } - - let list_of_connected_pids: Vec<_> = self.connected_players.iter().filter_map(|p| p.upgrade()).map(|p| p.pid).collect(); - - for other_connection in conns{ - let Some(other_conn) = other_connection.upgrade() else { - continue; - }; - - - let other_pid = other_conn.pid; - /*if other_pid == self.session.gathering.owner_pid && - joining_pid == self.session.gathering.owner_pid{ - continue; - }*/ - - for pid in &list_of_connected_pids { - other_conn.remote.process_notification_event(NotificationEvent { - pid_source: initiating_pid, - notif_type: 3001, - param_1: self.session.gathering.self_gid, - param_2: *pid, - str_param: join_msg.clone(), - param_3: self.connected_players.len() as _ - }).await; - } - } - - for old_conns in &old_particip{ - let Some(old_conns) = old_conns.upgrade() else { - continue; - }; - - let older_pid = old_conns.pid; - - - - initiating_user.remote.process_notification_event(NotificationEvent{ - pid_source: initiating_pid, - notif_type: 3001, - param_1: self.session.gathering.self_gid, - param_2: older_pid, - str_param: join_msg.clone(), - param_3: self.connected_players.len() as _ - }).await; - } - } - #[inline] - pub fn is_reachable(&self) -> bool{ - (if self.session.gathering.flags & PERSISTENT_GATHERING != 0{ - if !self.connected_players.is_empty(){ - true - } else { - self.session.open_participation - } - } else { - !self.connected_players.is_empty() - }) & !self.connected_players.is_empty() - } - #[inline] - pub fn is_joinable(&self) -> bool{ - self.is_reachable() && self.session.open_participation - } - - pub fn matches_criteria(&self, search_criteria: &MatchmakeSessionSearchCriteria) -> Result{ - // todo: implement the rest of the search criteria - - if search_criteria.vacant_only { - if (self.connected_players.len() as u16 + search_criteria.vacant_participants) > self.session.gathering.maximum_participants{ - return Ok(false); - } - } - - if search_criteria.exclude_locked{ - if !self.session.open_participation{ - return Ok(false); - } - } - - if search_criteria.exclude_system_password_set{ - if self.session.system_password_enabled{ - return Ok(false); - } - } - - if search_criteria.exclude_user_password_set{ - if self.session.user_password_enabled{ - return Ok(false); - } - } - - if !check_bounds_str(self.session.gathering.minimum_participants, &search_criteria.minimum_participants).ok_or(Core_InvalidArgument)? { - return Ok(false); - } - - if !check_bounds_str(self.session.gathering.maximum_participants, &search_criteria.maximum_participants).ok_or(Core_InvalidArgument)? { - return Ok(false); - } - - let game_mode: u32 = search_criteria.game_mode.parse().map_err(|_| Core_InvalidArgument)?; - - if self.session.gamemode != game_mode{ - return Ok(false); - } - - let mm_sys_type: u32 = search_criteria.matchmake_system_type.parse().map_err(|_| Core_InvalidArgument)?; - - if self.session.matchmake_system_type != mm_sys_type{ - return Ok(false); - } - - - if search_criteria.attribs.get(0).map(|str| str.parse().ok()).flatten() != self.session.attributes.get(0).map(|v| *v){ - return Ok(false); - } - if search_criteria.attribs.get(2).map(|str| str.parse().ok()).flatten() != self.session.attributes.get(2).map(|v| *v){ - return Ok(false); - } - if search_criteria.attribs.get(3).map(|str| str.parse().ok()).flatten() != self.session.attributes.get(3).map(|v| *v){ - return Ok(false); - } - - Ok(true) - } - - pub async fn migrate_ownership(&mut self, initiator_pid: u32) -> Result<(), ErrorCode>{ - let players: Vec<_> = self.connected_players.iter().filter_map(|p| p.upgrade()).collect(); - - let Some(new_owner) = players.iter().find(|p| p.pid != self.session.gathering.owner_pid) else { - self.session.gathering.owner_pid = 0; - - return Ok(()); - }; - - self.session.gathering.owner_pid = new_owner.pid; - - self.broadcast_notification(&NotificationEvent{ - pid_source: initiator_pid, - notif_type: OWNERSHIP_CHANGED, - param_1: self.session.gathering.self_gid, - param_2: new_owner.pid, - ..Default::default() - }).await; - - Ok(()) - } - - pub async fn migrate_host(&mut self, initiator_pid: u32) -> Result<(), ErrorCode>{ - let players: Vec<_> = self.connected_players.iter().filter_map(|p| p.upgrade()).collect(); - - self.session.gathering.host_pid = self.session.gathering.owner_pid; - - self.broadcast_notification(&NotificationEvent{ - pid_source: initiator_pid, - notif_type: HOST_CHANGED, - param_1: self.session.gathering.self_gid, - ..Default::default() - }).await; - - Ok(()) - } - - pub async fn remove_player_from_session(&mut self, pid: u32, message: &str) -> Result<(), ErrorCode>{ - self.connected_players.retain(|u| u.upgrade().is_some_and(|u| u.pid != pid)); - - self.session.participation_count = (self.connected_players.len() & u32::MAX as usize) as u32; - - if pid == self.session.gathering.owner_pid { - self.migrate_ownership(pid).await?; - } - - if pid == self.session.gathering.host_pid { - self.migrate_host(pid).await?; - } - - // todo: support DisconnectChangeOwner - - // todo: finish the rest of this - - for player in self.connected_players.iter().filter_map(|p| p.upgrade()){ - player.remote.process_notification_event(NotificationEvent{ - notif_type: 3008, - pid_source: pid, - param_1: self.session.gathering.self_gid, - param_2: pid, - str_param: message.to_owned(), - .. Default::default() - }).await; - } - - Ok(()) - } -} \ No newline at end of file diff --git a/src/nex/user.rs b/src/nex/user.rs deleted file mode 100644 index e166a80..0000000 --- a/src/nex/user.rs +++ /dev/null @@ -1,562 +0,0 @@ -use crate::define_rmc_proto; -use crate::nex::matchmake::{ExtendedMatchmakeSession, MatchmakeManager}; -use crate::nex::remote_console::RemoteConsole; -use crate::prudp::sockaddr::PRUDPSockAddr; -use crate::prudp::station_url::UrlOptions::{ - Address, NatFiltering, NatMapping, NatType, Port, PrincipalID, RVConnectionID, - -}; -use crate::prudp::station_url::{StationUrl}; -use crate::rmc::protocols::matchmake::{ - Matchmake, RawMatchmake, RawMatchmakeInfo, RemoteMatchmake, -}; -use crate::rmc::protocols::ranking::{Ranking, RawRanking, RawRankingInfo, RemoteRanking}; -use crate::rmc::protocols::matchmake_extension::{ - MatchmakeExtension, RawMatchmakeExtension, RawMatchmakeExtensionInfo, RemoteMatchmakeExtension, -}; -use crate::rmc::protocols::nat_traversal::{NatTraversal, RawNatTraversal, RawNatTraversalInfo, RemoteNatTraversal, RemoteNatTraversalConsole}; -use crate::rmc::protocols::secure::{RawSecure, RawSecureInfo, RemoteSecure, Secure}; -use crate::rmc::protocols::matchmake_ext::{MatchmakeExt, RawMatchmakeExt, RawMatchmakeExtInfo, RemoteMatchmakeExt}; -use crate::rmc::response::ErrorCode; -use crate::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession}; - -use crate::rmc::structures::qresult::QResult; -use macros::rmc_struct; -use std::sync::{Arc, Weak}; -use log::info; -use tokio::sync::{Mutex, RwLock}; -use crate::prudp::station_url::nat_types::PUBLIC; -use crate::rmc::protocols::notifications::{NotificationEvent, RemoteNotification}; -use crate::rmc::response::ErrorCode::{Core_Exception, Core_InvalidArgument, RendezVous_AccountExpired}; - -define_rmc_proto!( - proto UserProtocol{ - Secure, - MatchmakeExtension, - MatchmakeExt, - Matchmake, - NatTraversal, - Ranking - } -); - -#[rmc_struct(UserProtocol)] -pub struct User { - pub pid: u32, - pub ip: PRUDPSockAddr, - pub this: Weak, - pub remote: RemoteConsole, - pub station_url: RwLock>, - pub matchmake_manager: Arc, -} - -impl Secure for User { - async fn register( - &self, - station_urls: Vec, - ) -> 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 mut public_station: Option = None; - let mut private_station: Option = None; - - for station in station_urls { - let is_public = station.options.iter().any(|v| { - if let NatType(v) = v { - if *v & PUBLIC != 0 { - return true; - } - } - false - }); - - let Some(nat_filtering) = station.options.iter().find_map(|v| match v { - NatFiltering(v) => Some(v), - _ => None - }) else { - return Err(Core_Exception); - }; - - let Some(nat_mapping) = station.options.iter().find_map(|v| match v { - NatMapping(v) => Some(v), - _ => None - }) else { - return Err(Core_Exception); - }; - - if !is_public || (*nat_filtering == 0 && *nat_mapping == 0) { - private_station = Some(station.clone()); - } - - if is_public { - public_station = Some(station); - } - } - - let Some(mut private_station) = private_station else { - return Err(Core_Exception); - }; - - let mut public_station = if let Some(public_station) = public_station { - public_station - } else { - let mut public_station = private_station.clone(); - - public_station.options.retain(|v| { - match v { - Address(_) | Port(_) | NatFiltering(_) | NatMapping(_) | NatType(_) => false, - _ => true - } - }); - - public_station.options.push(Address(*self.ip.regular_socket_addr.ip())); - public_station.options.push(Port(self.ip.regular_socket_addr.port())); - public_station.options.push(NatFiltering(0)); - public_station.options.push(NatMapping(0)); - public_station.options.push(NatType(3)); - - public_station - }; - - let both = [&mut public_station, &mut private_station]; - - for station in both { - station.options.retain(|v| { - match v { - PrincipalID(_) | RVConnectionID(_) => false, - _ => true - } - }); - - station.options.push(PrincipalID(self.pid)); - station.options.push(RVConnectionID(cid)); - } - - - let mut lock = self.station_url.write().await; - - *lock = vec![ - public_station.clone(), - // private_station.clone() - ]; - - drop(lock); - - let result = QResult::success(ErrorCode::Core_Unknown); - - let out = public_station.to_string(); - - println!("out: {}", out); - - Ok((result, cid, public_station)) - } - - 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) -> Result, 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 { - 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 { - let session = self.matchmake_manager.get_session(join_session_param.gid).await?; - - let mut session = session.lock().await; - - 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 { - 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 { - 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 { - Ok(true) - } - async fn get_session_urls(&self, gid: u32) -> Result, 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, - param_2: self.pid, - param_3: 0, - str_param: "".to_string(), - }).await; - } - - if change_session_owner { - session.session.gathering.owner_pid = self.pid; - - - for player in &session.connected_players { - let Some(player) = player.upgrade() else { - continue; - }; - - player.remote.process_notification_event(NotificationEvent { - notif_type: 4000, - pid_source: self.pid, - param_1: gid, - param_2: self.pid, - param_3: 0, - str_param: "".to_string(), - }).await; - } - } - - Ok(()) - } - - async fn migrate_gathering_ownership(&self, gid: u32, candidates: Vec, _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, - param_2: *candidate, - param_3: 0, - str_param: "".to_string(), - }).await; - } - - Ok(()) - } -} - -impl MatchmakeExt for User { - async fn end_participation(&self, gid: u32, message: String) -> Result { - 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, 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(()) - } -} - -impl Ranking for User{ - -} \ No newline at end of file diff --git a/src/prudp/sockaddr.rs b/src/prudp/sockaddr.rs deleted file mode 100644 index 4e42fcc..0000000 --- a/src/prudp/sockaddr.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::io::Write; -use std::net::SocketAddrV4; -use hmac::{Hmac, Mac}; -use macros::RmcSerialize; -use crate::prudp::packet::VirtualPort; - -type Md5Hmac = Hmac; - -#[derive(Eq, PartialEq, Hash, Debug, Copy, Clone, Ord, PartialOrd, RmcSerialize)] -#[rmc_struct(0)] -pub struct PRUDPSockAddr{ - pub regular_socket_addr: SocketAddrV4, - pub virtual_port: VirtualPort -} - - - -impl PRUDPSockAddr{ - - pub fn new(regular_socket_addr: SocketAddrV4, virtual_port: VirtualPort) -> Self{ - Self{ - regular_socket_addr, - virtual_port - } - } - - pub(super) fn calculate_connection_signature(&self) -> [u8; 16] { - let mut hmac = Md5Hmac::new_from_slice(&[0; 16]).expect("fuck"); - - let mut data = self.regular_socket_addr.ip().octets().to_vec(); - //data.extend_from_slice(&self.regular_socket_addr.port().to_be_bytes()); - - 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 - } -} \ No newline at end of file diff --git a/src/prudp/station_url.rs b/src/prudp/station_url.rs deleted file mode 100644 index 441c1b3..0000000 --- a/src/prudp/station_url.rs +++ /dev/null @@ -1,198 +0,0 @@ -use std::net::Ipv4Addr; -use log::error; -use std::fmt::{Debug, Display, Formatter, Write}; -use std::io::Read; -use crate::prudp::station_url::Type::{PRUDP, PRUDPS, UDP}; -use crate::prudp::station_url::UrlOptions::{Address, ConnectionID, NatFiltering, NatMapping, NatType, Platform, PMP, Port, PrincipalID, RVConnectionID, StreamID, StreamType, UPNP, PID}; -use crate::rmc::structures::Error::StationUrlInvalid; -use crate::rmc::structures::RmcSerialize; -#[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(Ipv4Addr), - Port(u16), - StreamType(u8), - StreamID(u8), - ConnectionID(u8), - PrincipalID(u32), - 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 -} - -impl StationUrl{ - pub fn read_options(options: &str) -> Option>{ - 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 { - 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<'a> Into for &'a StationUrl{ - fn into(self) -> String { - let mut url = match self.url_type{ - UDP => "udp:/", - PRUDP => "prudp:/", - PRUDPS => "prudps:/" - }.to_owned(); - - for option in &self.options{ - match option{ - Address(v) => write!(url, "address={}", v).expect("failed to write"), - Port(v) => write!(url, "port={}", v).expect("failed to write"), - StreamType(v) => write!(url, "stream={}", v).expect("failed to write"), - StreamID(v) => write!(url, "sid={}", v).expect("failed to write"), - ConnectionID(v) => write!(url, "CID={}", v).expect("failed to write"), - PrincipalID(v) => write!(url, "PID={}", v).expect("failed to write"), - NatType(v) => write!(url, "type={}", v).expect("failed to write"), - NatMapping(v) => write!(url, "natm={}", v).expect("failed to write"), - NatFiltering(v) => write!(url, "natf={}", v).expect("failed to write"), - UPNP(v) => write!(url, "upnp={}", v).expect("failed to write"), - RVConnectionID(v) => write!(url, "RVCID={}", v).expect("failed to write"), - Platform(v) => write!(url, "pl={}", v).expect("failed to write"), - PMP(v) => write!(url, "pmp={}", v).expect("failed to write"), - PID(v) => write!(url, "PID={}", v).expect("failed to write"), - } - write!(url, ";").expect("failed to write"); - } - - url[0..url.len()-1].into() - } -} - -impl Display for StationUrl{ - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let str: String = self.into(); - - write!(f, "{}", str) - } - - -} - -impl RmcSerialize for StationUrl{ - fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { - let str = String::deserialize(reader)?; - - Self::try_from(str.as_str()).map_err(|_| StationUrlInvalid) - } - fn serialize(&self, writer: &mut dyn std::io::Write) -> crate::rmc::structures::Result<()> { - let str: String = self.into(); - - str.serialize(writer) - } -} - -impl Debug for StationUrl{ - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let str: String = self.into(); - f.write_str(&str) - } -} \ No newline at end of file diff --git a/src/reggie.rs b/src/reggie.rs deleted file mode 100644 index f57ebb0..0000000 --- a/src/reggie.rs +++ /dev/null @@ -1,82 +0,0 @@ -use std::{env, fs, io}; -use std::hash::Hash; -use std::io::{Error, ErrorKind}; -use std::net::{SocketAddrV4, ToSocketAddrs}; -use std::pin::Pin; -use std::sync::Arc; -use std::task::{Context, Poll}; -use futures::{SinkExt, StreamExt}; -use macros::{method_id, rmc_proto, rmc_struct, RmcSerialize}; -use once_cell::sync::Lazy; -use rustls::{ClientConfig, RootCertStore, ServerConfig}; -use rustls::client::WebPkiServerVerifier; -use rustls::server::WebPkiClientVerifier; -use rustls_pki_types::{CertificateDer, PrivateKeyDer, ServerName, TrustAnchor}; -use thiserror::Error; -use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf}; -use tokio::net::{TcpListener, TcpStream}; -use tokio_rustls::{TlsAcceptor, TlsConnector}; -use tokio_rustls::client::TlsStream; -use tokio_tungstenite::{connect_async, MaybeTlsStream, WebSocketStream}; -use tokio_tungstenite::tungstenite::Message; -use webpki::anchor_from_trusted_cert; -use rust_nex::common::setup; -use crate::define_rmc_proto; -use crate::nex::account::Account; -use crate::rmc::protocols::{new_rmc_gateway_connection, OnlyRemote, RmcCallable, RmcConnection}; -use crate::rmc::response::ErrorCode; -use crate::rmc::structures::RmcSerialize; - -pub trait UnitPacketRead: AsyncRead + Unpin{ - async fn read_buffer(&mut self) -> Result, 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 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 UnitPacketWrite for T{} - - - -#[rmc_proto(1)] -pub trait EdgeNodeManagement { - #[method_id(1)] - async fn get_url(&self, seed: u64) -> Result; -} - -define_rmc_proto!( - proto EdgeNodeHolder{ - EdgeNodeManagement - } -); - -#[derive(RmcSerialize, Debug)] -#[repr(u32)] -pub enum EdgeNodeHolderConnectOption{ - DontRegister = 0, - Register(SocketAddrV4) = 1 -} diff --git a/src/rmc/protocols/ranking.rs b/src/rmc/protocols/ranking.rs deleted file mode 100644 index 298922f..0000000 --- a/src/rmc/protocols/ranking.rs +++ /dev/null @@ -1,5 +0,0 @@ -use macros::rmc_proto; - -#[rmc_proto(112)] -pub trait Ranking{ -} \ No newline at end of file diff --git a/src/rmc/protocols/secure.rs b/src/rmc/protocols/secure.rs deleted file mode 100644 index 3071c3a..0000000 --- a/src/rmc/protocols/secure.rs +++ /dev/null @@ -1,12 +0,0 @@ -use macros::{method_id, rmc_proto}; -use crate::prudp::station_url::StationUrl; -use crate::rmc::response::ErrorCode; -use crate::rmc::structures::qresult::QResult; - -#[rmc_proto(11)] -pub trait Secure { - #[method_id(1)] - async fn register(&self, station_urls: Vec) -> Result<(QResult, u32, StationUrl), ErrorCode>; - #[method_id(7)] - async fn replace_url(&self, target: StationUrl, dest: StationUrl) -> Result<(), ErrorCode>; -} diff --git a/src/rmc/structures/any.rs b/src/rmc/structures/any.rs deleted file mode 100644 index d402f3e..0000000 --- a/src/rmc/structures/any.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::io::{Read, Write}; -use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; -use super::{Result, RmcSerialize}; - -#[derive(Debug, Default)] -pub struct Any{ - pub name: String, - pub data: Vec -} - -impl RmcSerialize for Any{ - fn serialize(&self, writer: &mut dyn Write) -> Result<()> { - self.name.serialize(writer)?; - - let u32_len = self.data.len() as u32; - - u32_len.serialize(writer)?; - u32_len.serialize(writer)?; - - self.data.serialize(writer)?; - - Ok(()) - } - fn deserialize(mut reader: &mut dyn Read) -> Result { - let name = String::deserialize(reader)?; - - // also length ? - let _len2: u32 = reader.read_struct(IS_BIG_ENDIAN)?; - let length: u32 = reader.read_struct(IS_BIG_ENDIAN)?; - - let mut data = vec![0; length as usize]; - - reader.read_exact(&mut data)?; - - Ok( - Any{ - name, - data - } - ) - } -} \ No newline at end of file diff --git a/src/rmc/structures/buffer.rs b/src/rmc/structures/buffer.rs deleted file mode 100644 index 5db53cf..0000000 --- a/src/rmc/structures/buffer.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::io::{Read, Write}; -use crate::rmc::structures::RmcSerialize; - - - -impl<'a> RmcSerialize for &'a [u8]{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - let u32_size = self.len() as u32; - writer.write(bytemuck::bytes_of(&u32_size))?; - writer.write(self)?; - - Ok(()) - } - - /// DO NOT USE (also maybe split off the serialize and deserialize functions at some point) - fn deserialize(_reader: &mut dyn Read) -> crate::rmc::structures::Result { - panic!("cannot deserialize to a u8 slice reference (use this ONLY for writing)") - } -} - -impl RmcSerialize for Box<[u8]>{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - (&self[..]).serialize(writer) - } - - fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { - Vec::deserialize(reader).map(|v| v.into_boxed_slice()) - } -} \ No newline at end of file diff --git a/src/rmc/structures/connection_data.rs b/src/rmc/structures/connection_data.rs deleted file mode 100644 index a949d91..0000000 --- a/src/rmc/structures/connection_data.rs +++ /dev/null @@ -1,14 +0,0 @@ - -use macros::RmcSerialize; -use crate::kerberos::KerberosDateTime; -use crate::rmc::structures::RmcSerialize; - -#[derive(Debug, RmcSerialize)] -#[rmc_struct(1)] -pub struct ConnectionData{ - pub station_url: String, - pub special_protocols: Vec, - pub special_station_url: String, - pub date_time: KerberosDateTime -} - diff --git a/src/rmc/structures/list.rs b/src/rmc/structures/list.rs deleted file mode 100644 index 870be65..0000000 --- a/src/rmc/structures/list.rs +++ /dev/null @@ -1,60 +0,0 @@ -use std::array::from_fn; -use std::io::{Read, Write}; -use std::mem::MaybeUninit; -use bytemuck::bytes_of; -use serde::Serialize; -use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; -use crate::rmc::structures::RmcSerialize; - - - -// this is also for implementing `Buffer` this is tecnically not the same as its handled internaly -// probably but as it has the same mapping it doesn't matter and simplifies things -impl RmcSerialize for Vec{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - let u32_len = self.len() as u32; - - writer.write_all(bytes_of(&u32_len))?; - for e in self{ - e.serialize(writer)?; - } - - Ok(()) - } - - fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result { - let len: u32 = reader.read_struct(IS_BIG_ENDIAN)?; - - let mut vec = Vec::with_capacity(len as usize); - - for _ in 0..len{ - vec.push(T::deserialize(reader)?); - } - - Ok(vec) - } -} - -impl RmcSerialize for [T; LEN]{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - for i in 0..LEN{ - self[i].serialize(writer)?; - } - - Ok(()) - } - - fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { - let mut arr = [const { MaybeUninit::::uninit() }; LEN]; - - for i in 0..LEN{ - arr[i] = MaybeUninit::new(T::deserialize(reader)?); - } - - // all of the elements are now initialized so it is safe to assume they are initialized - - let arr = arr.map(|v| unsafe{ v.assume_init() }); - - Ok(arr) - } -} diff --git a/src/rmc/structures/mod.rs b/src/rmc/structures/mod.rs deleted file mode 100644 index fd6d61d..0000000 --- a/src/rmc/structures/mod.rs +++ /dev/null @@ -1,60 +0,0 @@ -use std::io; -use std::io::{Read, Write}; -use std::string::FromUtf8Error; -use thiserror::Error; - -//ideas for the future: make a proc macro library which allows generation of struct reads - -#[derive(Error, Debug)] -pub enum Error{ - #[error("Io Error: {0}")] - Io(#[from] io::Error), - #[error("UTF8 conversion Error: {0}")] - Utf8(#[from] FromUtf8Error), - #[error("unexpected value: {0}")] - UnexpectedValue(u64), - #[error("version mismatch: {0}")] - VersionMismatch(u8), - #[error("an error occurred reading the station url")] - StationUrlInvalid -} - -pub type Result = std::result::Result; - -pub mod string; -pub mod any; -pub mod qresult; -pub mod buffer; -pub mod connection_data; -pub mod rmc_struct; -pub mod list; -pub mod qbuffer; -pub mod primitives; -pub mod matchmake; -pub mod variant; -pub mod ranking; -mod networking; - -pub trait RmcSerialize{ - fn serialize(&self, writer: &mut dyn Write) -> Result<()>; - fn deserialize(reader: &mut dyn Read) -> Result where Self: Sized; - - fn to_data(&self) -> Vec{ - let mut data = Vec::new(); - - self.serialize(&mut data).expect("out of memory or something"); - - data - } -} - -impl RmcSerialize for (){ - fn serialize(&self, writer: &mut dyn Write) -> Result<()> { - Ok(()) - } - fn deserialize(reader: &mut dyn Read) -> Result { - Ok(()) - } - - -} \ No newline at end of file diff --git a/src/rmc/structures/networking.rs b/src/rmc/structures/networking.rs deleted file mode 100644 index 9a11c5a..0000000 --- a/src/rmc/structures/networking.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::io::{Read, Write}; -use std::net::{Ipv4Addr, SocketAddrV4}; -use crate::prudp::packet::VirtualPort; -use crate::rmc::structures::RmcSerialize; - -impl RmcSerialize for SocketAddrV4{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - self.ip().to_bits().serialize(writer)?; - self.port().serialize(writer)?; - - Ok(()) - } - - fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { - let ip = u32::deserialize(reader)?; - let port = u16::deserialize(reader)?; - - Ok(SocketAddrV4::new(Ipv4Addr::from_bits(ip), port)) - } -} - - -impl RmcSerialize for VirtualPort{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - self.0.serialize(writer)?; - - Ok(()) - } - - fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { - Ok(Self(u8::deserialize(reader)?)) - } -} \ No newline at end of file diff --git a/src/rmc/structures/primitives.rs b/src/rmc/structures/primitives.rs deleted file mode 100644 index a834d9a..0000000 --- a/src/rmc/structures/primitives.rs +++ /dev/null @@ -1,241 +0,0 @@ -use std::io::{Read, Write}; -use bytemuck::bytes_of; -use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; -use crate::rmc::structures::RmcSerialize; - -impl RmcSerialize for u8{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - Ok(writer.write_all(bytes_of(self))?) - } - - fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result { - Ok(reader.read_struct(IS_BIG_ENDIAN)?) - } -} - -impl RmcSerialize for i8{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - Ok(writer.write_all(bytes_of(self))?) - } - - fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result { - Ok(reader.read_struct(IS_BIG_ENDIAN)?) - } -} - -impl RmcSerialize for u16{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - Ok(writer.write_all(bytes_of(self))?) - } - - fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result { - Ok(reader.read_struct(IS_BIG_ENDIAN)?) - } -} - -impl RmcSerialize for i16{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - Ok(writer.write_all(bytes_of(self))?) - } - - fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result { - Ok(reader.read_struct(IS_BIG_ENDIAN)?) - } -} - -impl RmcSerialize for u32{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - Ok(writer.write_all(bytes_of(self))?) - } - - fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result { - Ok(reader.read_struct(IS_BIG_ENDIAN)?) - } -} - -impl RmcSerialize for i32{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - Ok(writer.write_all(bytes_of(self))?) - } - - fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result { - Ok(reader.read_struct(IS_BIG_ENDIAN)?) - } -} - -impl RmcSerialize for u64{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - Ok(writer.write_all(bytes_of(self))?) - } - - fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result { - Ok(reader.read_struct(IS_BIG_ENDIAN)?) - } -} - -impl RmcSerialize for i64{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - Ok(writer.write_all(bytes_of(self))?) - } - - fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result { - Ok(reader.read_struct(IS_BIG_ENDIAN)?) - } -} - -impl RmcSerialize for f64{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - Ok(writer.write_all(bytes_of(self))?) - } - - fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result { - Ok(reader.read_struct(IS_BIG_ENDIAN)?) - } -} - -impl RmcSerialize for bool{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - match self{ - true => writer.write_all(&[1])?, - false => writer.write_all(&[0])?, - } - Ok(()) - } - - fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { - Ok(u8::deserialize(reader)? != 0) - } -} - - -impl RmcSerialize for (T, U){ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - self.0.serialize(writer)?; - self.1.serialize(writer)?; - Ok(()) - } - - fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { - let first = T::deserialize(reader)?; - let second = U::deserialize(reader)?; - - Ok((first, second)) - } -} - -impl RmcSerialize for (T, U, V){ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - self.0.serialize(writer)?; - self.1.serialize(writer)?; - self.2.serialize(writer)?; - Ok(()) - } - - fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { - let first = T::deserialize(reader)?; - let second = U::deserialize(reader)?; - let third = V::deserialize(reader)?; - - Ok((first, second, third)) - } -} - -impl RmcSerialize for (T, U, V, W){ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - self.0.serialize(writer)?; - self.1.serialize(writer)?; - self.2.serialize(writer)?; - self.3.serialize(writer)?; - Ok(()) - } - - fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { - let first = T::deserialize(reader)?; - let second = U::deserialize(reader)?; - let third = V::deserialize(reader)?; - let fourth = W::deserialize(reader)?; - - Ok((first, second, third, fourth)) - } -} - -impl RmcSerialize for (T, U, V, W, X){ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - self.0.serialize(writer)?; - self.1.serialize(writer)?; - self.2.serialize(writer)?; - self.3.serialize(writer)?; - self.4.serialize(writer)?; - - Ok(()) - } - - fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { - let first = T::deserialize(reader)?; - let second = U::deserialize(reader)?; - let third = V::deserialize(reader)?; - let fourth = W::deserialize(reader)?; - let fifth = X::deserialize(reader)?; - - Ok((first, second, third, fourth, fifth)) - } -} - -impl RmcSerialize for (T, U, V, W, X, Y){ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - self.0.serialize(writer)?; - self.1.serialize(writer)?; - self.2.serialize(writer)?; - self.3.serialize(writer)?; - self.4.serialize(writer)?; - self.5.serialize(writer)?; - - Ok(()) - } - - fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { - let first = T::deserialize(reader)?; - let second = U::deserialize(reader)?; - let third = V::deserialize(reader)?; - let fourth = W::deserialize(reader)?; - let fifth = X::deserialize(reader)?; - let sixth = Y::deserialize(reader)?; - - Ok((first, second, third, fourth, fifth, sixth)) - } -} - -impl RmcSerialize for (T, U, V, W, X, Y, Z){ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - self.0.serialize(writer)?; - self.1.serialize(writer)?; - self.2.serialize(writer)?; - self.3.serialize(writer)?; - self.4.serialize(writer)?; - self.5.serialize(writer)?; - self.6.serialize(writer)?; - - Ok(()) - } - - fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { - let first = T::deserialize(reader)?; - let second = U::deserialize(reader)?; - let third = V::deserialize(reader)?; - let fourth = W::deserialize(reader)?; - let fifth = X::deserialize(reader)?; - let sixth = Y::deserialize(reader)?; - let seventh = Z::deserialize(reader)?; - - Ok((first, second, third, fourth, fifth, sixth, seventh)) - } -} - -impl RmcSerialize for Box{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - self.as_ref().serialize(writer) - } - fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { - T::deserialize(reader).map(Box::new) - } -} \ No newline at end of file diff --git a/src/rmc/structures/qresult.rs b/src/rmc/structures/qresult.rs deleted file mode 100644 index 41a5263..0000000 --- a/src/rmc/structures/qresult.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::io::{Read, Write}; -use bytemuck::{bytes_of, Pod, Zeroable}; -use v_byte_macros::SwapEndian; -use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; -use crate::rmc::response::ErrorCode; -use crate::rmc::structures::{RmcSerialize, Result}; - -pub const ERROR_MASK: u32 = 1 << 31; - -#[derive(Pod, Zeroable, Copy, Clone, SwapEndian, Debug)] -#[repr(transparent)] -pub struct QResult(u32); - -impl QResult{ - pub fn success(error_code: ErrorCode) -> Self{ - let val: u32 = error_code.into(); - - Self(val & (!ERROR_MASK)) - } - - pub fn error(error_code: ErrorCode) -> Self{ - let val: u32 = error_code.into(); - - Self(val | ERROR_MASK) - } -} - -impl RmcSerialize for QResult{ - fn serialize(&self, writer: &mut dyn Write) -> Result<()> { - writer.write(bytes_of(self))?; - Ok(()) - } - - fn deserialize(mut reader: &mut dyn Read) -> Result { - Ok(reader.read_struct(IS_BIG_ENDIAN)?) - } -} \ No newline at end of file diff --git a/src/rmc/structures/ranking.rs b/src/rmc/structures/ranking.rs deleted file mode 100644 index c912d3f..0000000 --- a/src/rmc/structures/ranking.rs +++ /dev/null @@ -1,69 +0,0 @@ -use bytemuck::{Pod, Zeroable}; -use macros::RmcSerialize; -use crate::rmc::structures::qbuffer::QBuffer; - -#[derive(RmcSerialize, Debug)] -#[rmc_struct(0)] -struct UploadCompetitionData{ - winning_team/*?*/: u32, - splatfest_id/*?*/: u32, - unk_2/*?*/: u32, - unk_3: u32, - team_id_1: u8, - team_id_2: u8, - unk_5: u32, - player_data/*?*/: QBuffer, -} - -#[derive(Copy, Clone, Pod, Zeroable)] -#[repr(C)] -struct UserData{ - name: [u16; 0x10], -} - -#[cfg(test)] -mod test{ - use std::io::Cursor; - use bytemuck::from_bytes; - use tokio::io::AsyncReadExt; - use crate::rmc::structures::ranking::{UploadCompetitionData, UserData}; - use crate::rmc::structures::RmcSerialize; - - #[test] - fn test() { - let data: [u8; 0xBD] = [ - 0x00, 0xB8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x00, 0x1F, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x49, 0x00, - 0x7A, 0x00, 0x7A, 0x00, 0x79, 0x00, 0x53, 0x00, 0x50, 0x00, 0x46, 0x00, 0x4E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0xF2, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1F, 0x5E, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x90, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0A, 0x00, 0x00, 0x14, 0x87, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, - ]; - - let mut cursor = Cursor::new(data); - - let data = UploadCompetitionData::deserialize(&mut cursor).expect("unable to deserialize data"); - - let user_data: &UserData = from_bytes(&data.player_data.0[..size_of::()]); - - let pos = user_data.name.iter() - .position(|v| *v == 0x0000) - .unwrap_or(0x10); - - let mut name = user_data.name[0..pos].to_vec(); - - name.iter_mut().for_each(|v| *v = v.swap_bytes()); - - let name = String::from_utf16(&name).expect("unable to get name"); - - println!("{:?}", name); - - assert!(u8::deserialize(&mut cursor).is_err()) - } -} \ No newline at end of file diff --git a/src/rmc/structures/rmc_struct.rs b/src/rmc/structures/rmc_struct.rs deleted file mode 100644 index 8f51a5a..0000000 --- a/src/rmc/structures/rmc_struct.rs +++ /dev/null @@ -1,44 +0,0 @@ -use std::io::{Cursor, Read, Write}; -use bytemuck::bytes_of; -use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; -use crate::rmc::structures::Error::VersionMismatch; -use crate::rmc::structures::Result; - -#[repr(C, packed)] -struct StructureHeader{ - version: u8, - length: u32 -} - -pub fn write_struct(writer: &mut dyn Write, version: u8, pred: impl FnOnce(&mut Vec) -> Result<()> ) -> Result<()> { - writer.write_all(&[version])?; - - let mut scratch_space: Vec = Vec::new(); - - (pred)(&mut scratch_space)?; - - let u32_size = scratch_space.len() as u32; - - writer.write_all(bytes_of(&u32_size))?; - writer.write_all(&scratch_space)?; - - Ok(()) -} - -pub fn read_struct(mut reader: &mut dyn Read, version: u8, pred: impl FnOnce(&mut Cursor>) -> Result) -> Result { - let ver: u8 = reader.read_struct(IS_BIG_ENDIAN)?; - - if ver != version{ - return Err(VersionMismatch(ver)); - } - - let size: u32 = reader.read_struct(IS_BIG_ENDIAN)?; - - let mut vec = vec![0u8; size as usize]; - - reader.read_exact(&mut vec)?; - - let mut cursor = Cursor::new(vec); - - Ok(pred(&mut cursor)?) -} \ No newline at end of file diff --git a/src/rmc/structures/string.rs b/src/rmc/structures/string.rs deleted file mode 100644 index 690e6a1..0000000 --- a/src/rmc/structures/string.rs +++ /dev/null @@ -1,39 +0,0 @@ -use std::io::{Read, Write}; -use bytemuck::bytes_of; -use log::error; -use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; -use super::{Result, RmcSerialize}; - -impl RmcSerialize for String{ - fn deserialize(mut reader: &mut dyn Read) -> Result { - let len: u16 = reader.read_struct(IS_BIG_ENDIAN)?; - let mut data = vec![0; len as usize - 1]; - reader.read_exact(&mut data)?; - - let null: u8 = reader.read_struct(IS_BIG_ENDIAN)?; - if null != 0{ - error!("unable to find null terminator... continuing anyways"); - } - - Ok(String::from_utf8(data)?) - } - fn serialize(&self, writer: &mut dyn Write) -> Result<()> { - (&self[..]).serialize(writer) - } -} - -impl RmcSerialize for &str{ - fn deserialize(_reader: &mut dyn Read) -> Result { - panic!("cannot serialize to &str") - } - fn serialize(&self, writer: &mut dyn Write) -> Result<()> { - let u16_len: u16 = (self.len() + 1) as u16; - writer.write(bytes_of(&u16_len))?; - - writer.write(self.as_bytes())?; - writer.write(&[0])?; - - Ok(()) - } -} - diff --git a/src/rnex_proxy_common.rs b/src/rnex_proxy_common.rs deleted file mode 100644 index c300aa0..0000000 --- a/src/rnex_proxy_common.rs +++ /dev/null @@ -1,11 +0,0 @@ -use macros::RmcSerialize; -use crate::prudp::sockaddr::PRUDPSockAddr; - -#[derive(Debug, RmcSerialize)] -#[rmc_struct(0)] -pub struct ConnectionInitData{ - pub prudpsock_addr: PRUDPSockAddr, - pub pid: u32, - -} - diff --git a/src/web/mod.rs b/src/web/mod.rs deleted file mode 100644 index cd2bcf2..0000000 --- a/src/web/mod.rs +++ /dev/null @@ -1,92 +0,0 @@ -use std::sync::Arc; -use async_trait::async_trait; -use rocket::{get, routes, Request, State}; -use rocket::request::{FromRequest, Outcome}; -use rocket::serde::json::Json; -use tokio::task::JoinHandle; -use crate::nex::matchmake::MatchmakeManager; -use crate::rmc::protocols::notifications::NotificationEvent; - -struct RnexApiAuth; - -#[async_trait] -impl<'r> FromRequest<'r> for RnexApiAuth{ - - type Error = (); - async fn from_request<'a>(request: &'r Request<'a>) -> Outcome { - Outcome::Success(RnexApiAuth) - } -} - - -#[get("/gatherings")] -async fn gatherings(mmm: &State>) -> Json>{ - let matches = mmm.sessions.read().await; - - Json(matches.keys().map(|v| *v).collect()) -} - -#[get("/gathering//players")] -async fn players_in_match(mmm: &State>, gid: u32) -> Option>>{ - let mmm = mmm.sessions.read().await; - - let gathering = mmm.get(&gid)?; - - let gathering = gathering.clone(); - - drop(mmm); - - let gathering = gathering.lock().await; - - Some(Json(gathering.connected_players.iter().filter_map(|p| p.upgrade()).map(|p| p.pid).collect())) -} -/* -#[get("/player//disconnect")] -async fn disconnect_player(_auth: RnexApiAuth, mmm: &State>, pid: u32) -> Option<()>{ - // this doesnt work and is broken, there might be some other way to remotely close gatherings... - // also if anyone gets this working change it to POST cause the only reason its get is because - // that makes testing it easier - let mmm = mmm.users.read().await; - - for player in mmm.values().filter_map(|p| p.upgrade()).filter(|p| p.pid == pid) { - player.remote.get_connection().0.close_connection().await; - } - - - Some(()) -}*/ - -#[get("/gathering//close")] -async fn close_gathering(_auth: RnexApiAuth, mmm: &State>, gid: u32) -> Option<()>{ - // this doesnt work and is broken, there might be some other way to remotely close gatherings... - // also if anyone gets this working change it to POST cause the only reason its get is because - // that makes testing it easier - let mmm = mmm.sessions.read().await; - - let gathering = mmm.get(&gid)?; - - let gathering = gathering.clone(); - - drop(mmm); - - let gathering = gathering.lock().await; - - gathering.broadcast_notification(&NotificationEvent{ - pid_source: gathering.session.gathering.owner_pid, - notif_type: 109000, - param_1: gathering.session.gathering.self_gid, - ..Default::default() - }).await; - - Some(()) -} - -pub async fn start_web(mgr: Arc) -> JoinHandle<()> { - tokio::spawn(async move { - rocket::build() - .mount("/", routes![gatherings, players_in_match, close_gathering]) - .manage(mgr) - .launch().await - .expect("unable to start webserver"); - }) -} \ No newline at end of file diff --git a/test-edition.sh b/test-edition.sh new file mode 100755 index 0000000..ac3fc49 --- /dev/null +++ b/test-edition.sh @@ -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 test --features "$EDITION_FEATURES" --target x86_64-unknown-linux-musl