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..b7ebf9f 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" @@ -353,53 +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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" -dependencies = [ - "percent-encoding", - "time", - "version_check", -] - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -407,12 +169,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "cpufeatures" -version = "0.2.17" +name = "crc32fast" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ - "libc", + "cfg-if", ] [[package]] @@ -425,33 +187,6 @@ 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" -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", -] - [[package]] name = "deranged" version = "0.4.0" @@ -461,39 +196,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,22 +203,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", - "const-oid", "crypto-common", "subtle", ] -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.102", -] - [[package]] name = "dotenv" version = "0.15.0" @@ -524,206 +214,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" [[package]] -name = "dunce" -version = "1.0.5" +name = "flate2" +version = "1.1.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" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ - "cfg-if", + "crc32fast", + "miniz_oxide", ] -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.102", -] - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -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 +247,7 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", -] - -[[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 0.14.2+wasi-0.2.4", + "wasi", ] [[package]] @@ -763,74 +256,6 @@ 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" -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" - [[package]] name = "hex" version = "0.4.3" @@ -846,26 +271,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 +282,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" @@ -1063,140 +312,6 @@ dependencies = [ "cc", ] -[[package]] -name = "icu_collections" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" - -[[package]] -name = "icu_properties" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" -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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" - -[[package]] -name = "icu_provider" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" -dependencies = [ - "displaydoc", - "icu_locale_core", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "idna" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -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" -dependencies = [ - "equivalent", - "hashbrown 0.15.4", - "serde", -] - -[[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 +322,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 +338,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 +354,11 @@ 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" - -[[package]] -name = "litemap" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "lock_api" @@ -1354,46 +376,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 +411,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 +418,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -1438,127 +428,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 +445,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,56 +471,6 @@ 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" -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" - [[package]] name = "parking_lot" version = "0.12.4" @@ -1673,7 +491,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -1682,122 +500,18 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" -[[package]] -name = "pin-utils" -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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" -dependencies = [ - "zerovec", -] - [[package]] name = "powerfmt" version = "0.2.0" @@ -1813,16 +527,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 +537,62 @@ 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", + "prudpv0", + "prudpv1", + "rnex-core", + "tokio", +] + +[[package]] +name = "proxy-common" +version = "0.1.0" +dependencies = [ + "log", + "rnex-core", + "thiserror", + "tokio", +] + +[[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]] @@ -1906,12 +604,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "r-efi" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" - [[package]] name = "rand" version = "0.8.5" @@ -1919,18 +611,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_chacha", + "rand_core", ] [[package]] @@ -1940,17 +622,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[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 0.9.3", + "rand_core", ] [[package]] @@ -1959,16 +631,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", -] - -[[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" -dependencies = [ - "getrandom 0.3.3", + "getrandom", ] [[package]] @@ -1982,117 +645,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" @@ -2101,205 +660,54 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.16", + "getrandom", "libc", "untrusted", "windows-sys 0.52.0", ] [[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", + "rand", "rc4", - "reqwest", - "rocket", - "rsa", - "rustls", - "rustls-pki-types", - "rustls-webpki", - "serde", - "sha2", "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", @@ -2317,11 +725,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,56 +740,12 @@ 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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" -dependencies = [ - "windows-sys 0.59.0", -] - -[[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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "serde" version = "1.0.219" @@ -2400,71 +763,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", -] - -[[package]] -name = "serde_json" -version = "1.0.140" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" -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", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "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", + "syn 2.0.104", ] [[package]] @@ -2483,14 +782,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 +800,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,52 +812,12 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -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", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -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" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ - "loom", + "libc", + "windows-sys 0.59.0", ] [[package]] @@ -2587,69 +839,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" -source = "registry+https://github.com/rust-lang/crates.io-index" -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", -] - [[package]] name = "termcolor" version = "1.4.1" @@ -2676,17 +874,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]] @@ -2722,31 +910,24 @@ dependencies = [ "time-core", ] -[[package]] -name = "tinystr" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" -dependencies = [ - "displaydoc", - "zerovec", -] - [[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,297 +938,7 @@ 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", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" -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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489a59b6730eda1b0171fcfda8b121f4bee2b35cba8645ca35c5f7ba3eb736c1" -dependencies = [ - "futures-util", - "log", - "tokio", - "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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d" -dependencies = [ - "bytes", - "data-encoding", - "http 1.3.1", - "httparse", - "log", - "rand 0.9.1", - "sha1", - "thiserror", - "utf-8", + "syn 2.0.104", ] [[package]] @@ -3056,37 +947,12 @@ 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" @@ -3094,14 +960,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] -name = "url" -version = "2.5.4" +name = "ureq" +version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "d39cb1dbab692d82a977c0392ffac19e188bd9186a9f32806f0aaa859d75585a" dependencies = [ - "form_urlencoded", - "idna", + "base64", + "flate2", + "log", "percent-encoding", + "rustls", + "rustls-pki-types", + "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]] @@ -3111,32 +995,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] -name = "utf8_iter" -version = "1.0.4" -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,30 +1009,12 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[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 = "wasm-bindgen" version = "0.2.100" @@ -3190,23 +1037,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 +1059,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3240,43 +1074,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 +1091,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" @@ -3322,7 +1112,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.102", + "syn 2.0.104", ] [[package]] @@ -3333,25 +1123,14 @@ 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" - -[[package]] -name = "windows-registry" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bab093bdd303a1240bb99b8aba8ea8a69ee19d34c9e2ef9594e708a4878820" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-result" @@ -3377,7 +1156,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 +1165,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -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-targets", ] [[package]] @@ -3410,266 +1174,82 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.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", -] - -[[package]] -name = "writeable" -version = "0.6.1" -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", -] - -[[package]] -name = "yoke" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.102", - "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", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.102", - "synstructure", + "syn 2.0.104", ] [[package]] @@ -3677,36 +1257,3 @@ name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" - -[[package]] -name = "zerotrie" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.102", -] diff --git a/Cargo.toml b/Cargo.toml index dd12b89..9d6ea8a 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"] 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..1c025b0 --- /dev/null +++ b/editions.yaml @@ -0,0 +1,17 @@ +splatoon: + features: + - rmc_struct_header + - prudpv1 + settings: + AUTH_REPORT_VERSION: "branch:origin/project/wup-agmj build:3_8_15_2004_0" + RNEX_VIRTUAL_PORT_INSECURE: "1:10" + RNEX_VIRTUAL_PORT_SECURE: "1:10" + RNEX_DEFAULT_PORT: 6000 +friends: + features: + - friends + settings: + AUTH_REPORT_VERSION: "branch:origin/feature/45925_FixAutoReconnect build:3_10_11_2006_0" + RNEX_VIRTUAL_PORT_INSECURE: "1:10" + RNEX_VIRTUAL_PORT_SECURE: "1:10" + RNEX_DEFAULT_PORT: 6000 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..dca135c 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -8,10 +8,10 @@ edition = "2018" [lib] proc-macro = true +doctest = false [dependencies] quote = "1.0.38" proc-macro2 = "1.0.93" syn = { version = "2.0.98", features = ["full"] } -rand = "0.9.0" diff --git a/macros/src/lib.rs b/macros/src/lib.rs index ad77595..d088561 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -48,7 +48,7 @@ impl Parse for ProtoInputParams { fn gen_serialize_data_struct( s: DataStruct, struct_attr: Option<&Attribute>, -) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) { +) -> (proc_macro2::TokenStream, proc_macro2::TokenStream, proc_macro2::TokenStream) { let serialize_base_content = { let mut serialize_content = quote! {}; @@ -65,7 +65,7 @@ fn gen_serialize_data_struct( let ident = f.ident.as_ref().unwrap(); serialize_content.append_all(quote! { - self.#ident.serialize(writer)?; + rnex_core::rmc::structures::RmcSerialize::serialize(&self.#ident, writer)?; }) } @@ -119,6 +119,28 @@ fn gen_serialize_data_struct( } }; + let write_size = { + let mut size_content = quote! { 0 }; + + for f in &s.fields { + + + let ident = f.ident.as_ref().unwrap(); + + size_content.append_all(quote! { + + rnex_core::rmc::structures::RmcSerialize::serialize_write_size(&self.#ident)? + }) + } + + size_content + }; + let write_size = if let Some(_) = struct_attr { + quote!{ #write_size + if rnex_core::config::FEATURE_HAS_STRUCT_HEADER{ 5 } else { 0 } } + } else { + write_size + }; + + // generate base with extends stuff let serialize_base_content = if let Some(attr) = struct_attr { @@ -143,9 +165,18 @@ fn gen_serialize_data_struct( quote! { #pre_inner - rust_nex::rmc::structures::rmc_struct::write_struct(writer, #version, |mut writer|{ - #serialize_base_content - })?; + rnex_core::rmc::structures::rmc_struct::write_struct( + writer, + #version, + rnex_core::rmc::structures::helpers::len_of_write( + |writer|{ + #serialize_base_content + } + ), + |writer|{ + #serialize_base_content + } + )?; Ok(()) } @@ -176,7 +207,7 @@ fn gen_serialize_data_struct( quote! { #pre_inner - Ok(rust_nex::rmc::structures::rmc_struct::read_struct(reader, #version, move |mut reader|{ + Ok(rnex_core::rmc::structures::rmc_struct::read_struct(reader, #version, move |mut reader|{ #deserialize_base_content })?) } @@ -184,7 +215,13 @@ fn gen_serialize_data_struct( deserialize_base_content }; - (serialize_base_content, deserialize_base_content) + let write_size = quote!{ + fn serialize_write_size(&self) -> rnex_core::rmc::structures::Result{ + Ok(#write_size) + } + }; + + (serialize_base_content, deserialize_base_content, write_size) } #[proc_macro_derive(RmcSerialize, attributes(extends, rmc_struct))] @@ -210,7 +247,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { panic!("rmc struct type MUST be a struct"); };*/ - let (serialize_base_content, deserialize_base_content) = match derive_input.data { + let (serialize_base_content, deserialize_base_content, write_size) = match derive_input.data { Data::Struct(s) => gen_serialize_data_struct(s, struct_attr), Data::Enum(e) => { let Some(repr_attr) = repr_attr else { @@ -221,6 +258,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { let mut inner_match_de = quote! {}; let mut inner_match_se = quote! {}; + //let mut inner_match_len = quote!{}; for variant in e.variants { let Some((_, val)) = variant.discriminant else { @@ -235,7 +273,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { let name = &field.ident; base.append_all(quote!{ - #name: <#ty as rust_nex::rmc::structures::RmcSerialize>::deserialize(reader)?, + #name: <#ty as rnex_core::rmc::structures::RmcSerialize>::deserialize(reader)?, }); } @@ -248,7 +286,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { let ty = &field.ty; base.append_all(quote!{ - <#ty as rust_nex::rmc::structures::RmcSerialize>::deserialize(reader)?, + <#ty as rnex_core::rmc::structures::RmcSerialize>::deserialize(reader)?, }); } @@ -260,7 +298,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { }; let mut se_with_fields = quote! { - <#ty as rust_nex::rmc::structures::RmcSerialize>::serialize(&#val, writer)?; + <#ty as rnex_core::rmc::structures::RmcSerialize>::serialize(&#val, writer)?; }; match &variant.fields { @@ -270,7 +308,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { let name = &field.ident; se_with_fields.append_all(quote!{ - <#ty as rust_nex::rmc::structures::RmcSerialize>::serialize(#name ,writer)?; + <#ty as rnex_core::rmc::structures::RmcSerialize>::serialize(#name ,writer)?; }); } } @@ -281,7 +319,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { let ident = Ident::new(&format!("val_{}", i), Span::call_site()); se_with_fields.append_all(quote!{ - <#ty as rust_nex::rmc::structures::RmcSerialize>::serialize(#ident, writer)?; + <#ty as rnex_core::rmc::structures::RmcSerialize>::serialize(#ident, writer)?; }); } } @@ -344,15 +382,15 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { }; let deserialize_base_content = quote! { - let val: Self = match <#ty as rust_nex::rmc::structures::RmcSerialize>::deserialize(reader)?{ + let val: Self = match <#ty as rnex_core::rmc::structures::RmcSerialize>::deserialize(reader)?{ #inner_match_de - v => return Err(rust_nex::rmc::structures::Error::UnexpectedValue(v as _)) + v => return Err(rnex_core::rmc::structures::Error::UnexpectedValue(v as _)) }; Ok(val) }; - (serialize_base_content, deserialize_base_content) + (serialize_base_content, deserialize_base_content, quote!{}) } Data::Union(_) => { unimplemented!() @@ -364,16 +402,17 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { let ident = derive_input.ident; let tokens = quote! { - impl rust_nex::rmc::structures::RmcSerialize for #ident{ - fn serialize(&self, writer: &mut dyn ::std::io::Write) -> rust_nex::rmc::structures::Result<()>{ + impl rnex_core::rmc::structures::RmcSerialize for #ident{ + #[inline(always)] + fn serialize(&self, writer: &mut impl ::std::io::Write) -> rnex_core::rmc::structures::Result<()>{ #serialize_base_content - - } - - fn deserialize(reader: &mut dyn ::std::io::Read) -> rust_nex::rmc::structures::Result{ + #[inline(always)] + fn deserialize(reader: &mut impl ::std::io::Read) -> rnex_core::rmc::structures::Result{ #deserialize_base_content } + + #write_size } }; @@ -511,8 +550,8 @@ pub fn rmc_struct(attr: TokenStream, input: TokenStream) -> TokenStream { } - impl rust_nex::rmc::protocols::RmcCallable for #struct_name{ - async fn rmc_call(&self, remote_response_connection: &rust_nex::util::SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec){ + 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..7dd7bf1 100644 --- a/macros/src/protos.rs +++ b/macros/src/protos.rs @@ -39,6 +39,7 @@ impl RmcProtocolData{ // boilerplate tokens which all raw traits need quote!{ #[doc(hidden)] + #[allow(unused_must_use)] pub trait #raw_name: #name }.to_tokens(tokens); @@ -54,6 +55,7 @@ impl RmcProtocolData{ let raw_name = Ident::new(&format!("raw_{}", name), name.span()); quote!{ + #[inline(always)] async fn #raw_name }.to_tokens(tokens); @@ -73,7 +75,7 @@ impl RmcProtocolData{ for (param_name, param_type) in parameters{ quote!{ let Ok(#param_name) = - <#param_type as rust_nex::rmc::structures::RmcSerialize>::deserialize( + <#param_type as rnex_core::rmc::structures::RmcSerialize>::deserialize( &mut cursor ) else }.to_tokens(tokens); @@ -84,7 +86,7 @@ impl RmcProtocolData{ quote! { { log::error!(#error_msg); - return Err(rust_nex::rmc::response::ErrorCode::Core_InvalidArgument); + return Err(rnex_core::rmc::response::ErrorCode::Core_InvalidArgument); }; }.to_tokens(tokens) } else { @@ -116,7 +118,7 @@ impl RmcProtocolData{ quote!{ let retval = retval?; let mut vec = Vec::new(); - rust_nex::rmc::structures::RmcSerialize::serialize(&retval, &mut vec).ok(); + rnex_core::rmc::structures::RmcSerialize::serialize(&retval, &mut vec).ok(); Ok(vec) }.to_tokens(tokens); } @@ -124,9 +126,10 @@ impl RmcProtocolData{ } quote!{ + #[inline(always)] async fn rmc_call_proto( &self, - remote_response_connection: &rust_nex::util::SendingBufferConnection, + remote_response_connection: &rnex_core::util::SendingBufferConnection, method_id: u32, call_id: u32, data: Vec, @@ -165,7 +168,7 @@ impl RmcProtocolData{ }.to_tokens(tokens); if self.has_returns { quote! { - Err(rust_nex::rmc::response::ErrorCode::Core_NotImplemented) + Err(rnex_core::rmc::response::ErrorCode::Core_NotImplemented) }.to_tokens(tokens); } }); @@ -176,7 +179,7 @@ impl RmcProtocolData{ if *has_returns{ quote!{ - rust_nex::rmc::response::send_result( + rnex_core::rmc::response::send_result( remote_response_connection, ret, #id, @@ -209,7 +212,8 @@ impl RmcProtocolData{ // boilerplate tokens which all raw traits need quote!{ #[doc(hidden)] - pub trait #remote_name: rust_nex::rmc::protocols::HasRmcConnection + #[allow(unused_must_use)] + pub trait #remote_name: rnex_core::rmc::protocols::HasRmcConnection }.to_tokens(tokens); // generate the body of the raw protocol trait @@ -247,12 +251,12 @@ impl RmcProtocolData{ for (param_name, param_type) in parameters{ quote!{ - rust_nex::result::ResultExtension::display_err_or_some( - <#param_type as rust_nex::rmc::structures::RmcSerialize>::serialize( + rnex_core::result::ResultExtension::display_err_or_some( + <#param_type as rnex_core::rmc::structures::RmcSerialize>::serialize( &#param_name, &mut cursor ) - ).ok_or(rust_nex::rmc::response::ErrorCode::Core_InvalidArgument) + ).ok_or(rnex_core::rmc::response::ErrorCode::Core_InvalidArgument) }.to_tokens(tokens); if self.has_returns { quote! { @@ -268,25 +272,25 @@ impl RmcProtocolData{ quote!{ let call_id = rand::random(); - let message = rust_nex::rmc::message::RMCMessage{ + let message = rnex_core::rmc::message::RMCMessage{ call_id, method_id: #method_id, protocol_id: #proto_id, rest_of_data: send_data }; - let rmc_conn = ::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); @@ -325,10 +329,5 @@ impl ToTokens for RmcProtocolData{ self.generate_raw_trait(tokens); self.generate_raw_info(tokens); self.generate_raw_remote_trait(tokens); - - - - } -} - +} \ No newline at end of file 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..6ba9ba7 --- /dev/null +++ b/proxy-common/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "proxy-common" +version = "0.1.0" +edition = "2024" + +[dependencies] +thiserror = "2.0.12" +rnex-core = { path = "../rnex-core", version = "0.1.1" } +tokio = { version = "1.47.0", features = ["full"] } +log = "0.4.25" diff --git a/proxy-common/src/lib.rs b/proxy-common/src/lib.rs new file mode 100644 index 0000000..93c16c8 --- /dev/null +++ b/proxy-common/src/lib.rs @@ -0,0 +1,215 @@ +use log::{error, info}; +use rnex_core::{ + executables::common::{OWN_IP_PUBLIC, try_get_ip}, + prudp::{socket_addr::PRUDPSockAddr, virtual_port::VirtualPort}, + reggie::{RemoteEdgeNodeHolder, UnitPacketWrite}, + rmc::{ + protocols::{ + OnlyRemote, RemoteDisconnectable, RmcCallable, RmcConnection, RmcPureRemoteObject, + new_rmc_gateway_connection, + }, + structures::RmcSerialize, + }, + rnex_proxy_common::ConnectionInitData, + util::{SendingBufferConnection, SplittableBufferConnection}, +}; +use std::{ + env::{self, VarError}, + error, + net::{AddrParseError, IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}, + ops::Deref, + panic, + str::FromStr, + sync::{Arc, LazyLock}, +}; +use thiserror::Error; +use tokio::net::TcpStream; + +const RNEX_DEFAULT_PORT: u16 = match u16::from_str_radix(env!("RNEX_DEFAULT_PORT"), 10) { + Ok(v) => v, + Err(_) => panic!("unable to get default port from env"), +}; + +#[derive(Error, Debug)] +pub enum Error { + #[error("error getting environment variable \"{0}\": {1}")] + UnableToGetEnv(&'static str, VarError), + #[error("error parsing ip address environment variable \"{0}\": {1}")] + AddrParse(&'static str, AddrParseError), + #[error( + "error error getting public ip address: \n\tattempted to read from env var \"SERVER_IP_PUBLIC\" and got: {0} \n\tattempted to request from internet and failed with: {1}" + )] + PubAddrGetErr(Box, 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, RNEX_DEFAULT_PORT)) + .map_err(move |v| Error::PubAddrGetErr(Box::new(e), v))?, + }; + + Ok(Self { + forward_destination: try_get_env("FORWARD_DESTINATION")?, + edge_node_holder: try_get_env("EDGE_NODE_HOLDER")?, + self_private, + self_public, + virtual_port: match prox_ty { + ProxyType::Insecure => *VIRTUAL_PORT_INSECURE, + ProxyType::Secure => *VIRTUAL_PORT_SECURE, + }, + }) + } +} + +struct OnRemoteDrop(T, 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: u32, +) -> 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; + } + }; + + if let Err(e) = stream + .send_buffer( + &ConnectionInitData { + prudpsock_addr: addr, + pid: pid, + } + .to_data() + .unwrap(), + ) + .await + { + error!("unable to send establishment data to backend: {}", e); + return None; + }; + + Some(stream.into()) +} + +#[cfg(test)] +mod test { + use crate::{VIRTUAL_PORT_INSECURE, VIRTUAL_PORT_SECURE}; + + fn test_virtual_port_correct() { + println!("{:?}", VIRTUAL_PORT_INSECURE); + println!("{:?}", VIRTUAL_PORT_SECURE); + } +} diff --git a/proxy/Cargo.toml b/proxy/Cargo.toml new file mode 100644 index 0000000..fa56ab2 --- /dev/null +++ b/proxy/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "proxy" +version = "0.1.0" +edition = "2024" + +[dependencies] +tokio = { version = "1.47.0", features = ["full"] } +prudpv0 = { path = "../prudpv0", optional = true } +prudpv1 = { path = "../prudpv1", optional = true } +proxy-common = { path = "../proxy-common" } +cfg-if = "1.0.4" +rnex-core = { path = "../rnex-core", version = "0.1.1" } +log = "0.4.25" + +[features] +prudpv0 = ["dep:prudpv0"] +prudpv1 = ["dep:prudpv1"] +friends = ["prudpv0", "prudpv0/friends"] +splatoon = ["prudpv1"] + + +[[bin]] +name = "proxy_insecure" +path = "src/insecure.rs" + +[[bin]] +name = "proxy_secure" +path = "src/secure.rs" 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..2c52a9a --- /dev/null +++ b/proxy/src/lib.rs @@ -0,0 +1,19 @@ +use std::process::abort; + +use cfg_if::cfg_if; +use log::error; + +cfg_if! { + if #[cfg(feature = "prudpv0")]{ + pub use prudpv0::*; + } else if #[cfg(feature = "prudpv1")] { + pub use prudpv1::*; + } else { + compile_error!("no proxy type has been set"); + } +} + +pub fn edge_node_dc_callback() { + error!("disconnected from node holder, aborting!"); + abort() +} 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/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..b11af1d --- /dev/null +++ b/prudpv0/src/crypto/friends_common.rs @@ -0,0 +1,5 @@ +use hmac::Hmac; +use md5::Md5; + +pub const ACCESS_KEY: &str = "ridfebb9"; +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..e94ab46 --- /dev/null +++ b/prudpv0/src/packet.rs @@ -0,0 +1,405 @@ +use std::mem::transmute; + +use bytemuck::{Pod, Zeroable, try_from_bytes, try_from_bytes_mut}; +use log::{error, info, warn}; +use rnex_core::prudp::{ + types_flags::{ + self, TypesFlags, + flags::{HAS_SIZE, NEED_ACK}, + types::{CONNECT, DATA, DISCONNECT, PING, SYN}, + }, + virtual_port::VirtualPort, +}; + +use crate::crypto::{Crypto, CryptoInstance}; + +#[repr(C, packed)] +#[derive(Clone, Copy, Pod, Zeroable, Debug)] +pub struct PRUDPV0Header { + pub source: VirtualPort, + pub destination: VirtualPort, + pub type_flags: TypesFlags, + pub session_id: u8, + pub packet_signature: [u8; 4], + pub sequence_id: u16, +} +#[repr(transparent)] +pub struct PRUDPV0Packet>(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"), + ); + + 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..aa603f7 --- /dev/null +++ b/prudpv0/src/server.rs @@ -0,0 +1,505 @@ +use std::{ + collections::HashMap, + hash::Hash, + net::{Ipv4Addr, SocketAddr, SocketAddrV4}, + sync::{ + Arc, LazyLock, Weak, + atomic::{AtomicBool, AtomicU32}, + }, + thread::sleep, + time::Duration, +}; + +use log::{error, info, warn}; +use proxy_common::{ProxyStartupParam, new_backend_connection}; +use rnex_core::{ + executables::common::{OWN_IP_PRIVATE, SERVER_PORT}, + prudp::{ + socket_addr::PRUDPSockAddr, + types_flags::{ + TypesFlags, + flags::{ACK, HAS_SIZE, NEED_ACK, RELIABLE}, + types::{CONNECT, DATA, DISCONNECT, PING, SYN}, + }, + virtual_port::VirtualPort, + }, + rnex_proxy_common::ConnectionInitData, + util::{SendingBufferConnection, SplittableBufferConnection}, +}; +use tokio::{ + net::{TcpSocket, UdpSocket}, + spawn, + sync::{Mutex, RwLock}, + time::Instant, +}; + +use crate::{ + crypto::{Crypto, CryptoInstance}, + packet::{ + PRUDPV0Header, PRUDPV0Packet, new_connect_packet, new_data_packet, new_disconnect_packet, + new_ping_packet, new_syn_packet, precalc_size, + }, +}; + +pub struct InternalConnection { + 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 seq = inner.server_packet_counter; + let packet = new_data_packet( + NEED_ACK | RELIABLE, + self.param.virtual_port, + conn.addr.virtual_port, + data, + inner.server_packet_counter, + conn.session_id, + 0, + &mut inner.crypto_instance, + &self.crypto, + ); + inner.server_packet_counter += 1; + + let packet = Arc::new(packet); + let packet_ref = Arc::downgrade(&packet); + + inner.unacknowledged_packets.insert(seq, packet); + + drop(inner); + + let conn = Arc::downgrade(&conn); + let this = Arc::downgrade(&self); + + spawn(async move { + for n in 0..5 { + let Some(data) = packet_ref.upgrade() else { + return; + }; + let Some(conn) = conn.upgrade() else { + return; + }; + let Some(this) = this.upgrade() else { + return; + }; + info!("send attempt {}", n); + + self.socket + .send_to(&data, conn.addr.regular_socket_addr) + .await; + + break; + } + }); + } + async fn connection_thread( + self: Arc, + 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: Arc>) { + loop { + sleep(Duration::from_secs(3)); + let mut inner = conn.inner.lock().await; + + if (Instant::now() - inner.last_action).as_secs() > 5 { + warn!("connection exceeded silence limit, sending ping"); + let packet = new_ping_packet( + NEED_ACK, + self.param.virtual_port, + conn.addr.virtual_port, + 0, + conn.session_id, + &mut inner.crypto_instance, + &self.crypto, + ); + + self.socket + .send_to(&packet, conn.addr.regular_socket_addr) + .await; + } + + if (Instant::now() - inner.last_action).as_secs() > 15 { + warn!("client timed out..."); + + let packet = new_disconnect_packet( + NEED_ACK, + self.param.virtual_port, + conn.addr.virtual_port, + 0, + conn.session_id, + &mut inner.crypto_instance, + &self.crypto, + ); + + self.socket + .send_to(&packet, conn.addr.regular_socket_addr) + .await; + self.socket + .send_to(&packet, conn.addr.regular_socket_addr) + .await; + self.socket + .send_to(&packet, conn.addr.regular_socket_addr) + .await; + drop(inner); + + let mut conns = self.connections.write().await; + conns.remove(&conn.addr); + drop(conns); + break; + } + + drop(inner); + } + } + async fn handle_syn(self: Arc, 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(); + let buf_conn = new_backend_connection(&self.param, addr, pid).await; + let Some(buf_conn) = buf_conn else { + error!("unable to connect to backend"); + return; + }; + + let header = packet.header().expect("header should be validated by now"); + + let conn = Arc::new(Connection { + target: buf_conn.duplicate_sender(), + remote_signat, + self_signat, + addr, + session_id: header.session_id, + alive: AtomicBool::new(true), + inner: Mutex::new(InternalConnection { + last_action: Instant::now(), + crypto_instance: ci, + client_packet_counter: 2, + server_packet_counter: 1, + unacknowledged_packets: HashMap::new(), + packet_queue: HashMap::new(), + }), + }); + + let mut conns = self.connections.write().await; + conns.insert(addr, conn.clone()); + drop(conns); + + spawn({ + let this = self.clone(); + let conn = Arc::downgrade(&conn); + this.connection_thread(conn, buf_conn) + }); + spawn({ + let this = self.clone(); + let conn = conn.clone(); + this.timeout_thread(conn) + }); + + let packet = new_connect_packet( + ACK, + header.destination, + header.source, + self_signat, + remote_signat, + packet.header().unwrap().session_id, + &data, + &self.crypto, + ); + + info!("sending back connection accept"); + self.socket.send_to(&packet, addr.regular_socket_addr).await; + } + async fn handle_data(self: Arc, 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).await else { + warn!("data packet on inactive connection from: {:?}", addr); + return; + }; + info!("frag: {}", frag_id); + let mut conn = res.inner.lock().await; + let ack = new_data_packet( + ACK, + self.param.virtual_port, + res.addr.virtual_port, + &[], + header.sequence_id, + header.session_id, + *frag_id, + &mut conn.crypto_instance, + &self.crypto, + ); + self.socket.send_to(&ack, addr.regular_socket_addr).await; + conn.packet_queue.insert( + packet.header().unwrap().sequence_id, + (Instant::now(), packet), + ); + while let Some((_, mut packet)) = { + let ctr = conn.client_packet_counter; + conn.packet_queue.remove(&ctr) + } { + info!("processing packet: {}", conn.client_packet_counter); + let Some(payload) = packet.payload_mut() else { + //todo: at this point the stream would have been broken, we should probably disconnect the client + warn!("invalid packet from: {:?}", addr); + return; + }; + + conn.crypto_instance.decrypt_incoming(payload); + + res.target.send(payload.to_owned()).await; + conn.client_packet_counter += 1; + } + info!("finished handeling packets, dropping inner connection"); + drop(conn); + } + + async fn handle_ping(self: Arc, mut packet: PRUDPV0Packet>, addr: PRUDPSockAddr) { + info!("got ping"); + let header = packet.header().unwrap(); + + let Some(conn) = self.get_connection(addr).await else { + warn!("ping on inactive connection: {:?}", addr); + return; + }; + let mut inner = conn.inner.lock().await; + let packet = new_ping_packet( + ACK, + self.param.virtual_port, + addr.virtual_port, + header.sequence_id, + header.session_id, + &mut inner.crypto_instance, + &self.crypto, + ); + drop(inner); + + self.socket.send_to(&packet, addr.regular_socket_addr).await; + } + async fn handle_disconnect( + self: Arc, + mut packet: PRUDPV0Packet>, + addr: PRUDPSockAddr, + ) { + info!("got disconnect"); + let header = packet.header().unwrap(); + + let Some(conn) = self.get_connection(addr).await else { + warn!("ping on inactive connection: {:?}", addr); + return; + }; + let mut inner = conn.inner.lock().await; + let packet = new_disconnect_packet( + ACK, + self.param.virtual_port, + addr.virtual_port, + header.sequence_id, + header.session_id, + &mut inner.crypto_instance, + &self.crypto, + ); + drop(inner); + + self.socket.send_to(&packet, addr.regular_socket_addr).await; + self.socket.send_to(&packet, addr.regular_socket_addr).await; + self.socket.send_to(&packet, addr.regular_socket_addr).await; + } + async fn get_connection(&self, addr: PRUDPSockAddr) -> Option>> { + 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(addr, header.source); + + if let Some(conn) = self.get_connection(addr).await { + let mut inner = conn.inner.lock().await; + inner.last_action = Instant::now(); + drop(inner); + }; + if header.type_flags.get_flags() & ACK != 0 { + info!("got ack(acks are ignored for now)"); + return; + } + println!("{:?}", header); + match header.type_flags.get_types() { + SYN => { + self.handle_syn(packet, addr).await; + } + CONNECT => { + self.handle_connect(packet, addr).await; + } + DATA => { + self.handle_data(packet, addr).await; + } + PING => { + self.handle_ping(packet, addr).await; + } + DISCONNECT => { + self.handle_disconnect(packet, addr).await; + } + v => { + println!("unimplemented packed type: {}", v); + } + } + } + pub async fn run_task(self: Arc) { + 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..975f837 100644 --- a/src/executables/proxy_secure.rs +++ b/prudpv1/src/executables/proxy_insecure.rs @@ -1,53 +1,49 @@ +use crate::executables::common::{EDGE_NODE_HOLDER, FORWARD_DESTINATION}; +use crate::prudp::router::Router; +use crate::prudp::unsecure::Unsecure; +use log::error; +use rnex_core::common::setup; +use rnex_core::executables::common::{OWN_IP_PRIVATE, OWN_IP_PUBLIC, SERVER_PORT}; +use rnex_core::prudp::virtual_port::VirtualPort; +use rnex_core::reggie::EdgeNodeHolderConnectOption::Register; +use rnex_core::reggie::RemoteEdgeNodeHolder; +use rnex_core::reggie::UnitPacketRead; +use rnex_core::reggie::UnitPacketWrite; +use rnex_core::rmc::protocols::{OnlyRemote, new_rmc_gateway_connection}; +use rnex_core::rmc::structures::RmcSerialize; +use rnex_core::rnex_proxy_common::ConnectionInitData; +use rnex_core::util::SplittableBufferConnection; use std::net::SocketAddrV4; use std::sync::Arc; use std::time::Duration; -use futures::future::Remote; -use log::{error, warn}; -use macros::rmc_struct; use tokio::net::TcpStream; -use tokio::sync::RwLock; use tokio::task; use tokio::time::sleep; -use tokio_rustls::client::TlsStream; -use tokio_tungstenite::MaybeTlsStream; -use rust_nex::common::setup; -use rust_nex::executables::common::{AUTH_SERVER_ACCOUNT, FORWARD_DESTINATION, OWN_IP_PRIVATE, OWN_IP_PUBLIC, SECURE_EDGE_NODE_HOLDER, SECURE_SERVER_ACCOUNT, SERVER_PORT}; -use rust_nex::prudp::packet::VirtualPort; -use rust_nex::prudp::router::Router; -use rust_nex::prudp::secure::Secure; -use rust_nex::prudp::unsecure::Unsecure; -use rust_nex::reggie::EdgeNodeHolderConnectOption::{DontRegister, Register}; -use rust_nex::rmc::response::ErrorCode; -use rust_nex::rnex_proxy_common::ConnectionInitData; -use rust_nex::reggie::{RemoteEdgeNodeHolder, UnitPacketWrite}; -use rust_nex::rmc::structures::RmcSerialize; -use rust_nex::reggie::UnitPacketRead; -use rust_nex::rmc::protocols::{new_rmc_gateway_connection, OnlyRemote, RemoteInstantiatable}; -use rust_nex::util::SplittableBufferConnection; -#[tokio::main] -async fn main() { - setup(); - - let conn = tokio::net::TcpStream::connect(&*SECURE_EDGE_NODE_HOLDER).await.unwrap(); +pub async fn start() { + /*let conn = tokio::net::TcpStream::connect(&*EDGE_NODE_HOLDER) + .await + .unwrap(); let conn: SplittableBufferConnection = conn.into(); - conn.send(Register(SocketAddrV4::new(*OWN_IP_PUBLIC, *SERVER_PORT)).to_data()).await; - - let conn = new_rmc_gateway_connection(conn, |r| Arc::new(OnlyRemote::::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("6f599f81")) .await .expect("unable to add socket"); @@ -60,8 +56,7 @@ async fn main() { }; task::spawn(async move { - let mut stream - = match TcpStream::connect(*FORWARD_DESTINATION).await { + let mut stream = match TcpStream::connect(*FORWARD_DESTINATION).await { Ok(v) => v, Err(e) => { error!("unable to connect: {}", e); @@ -69,26 +64,31 @@ async fn main() { } }; - if let Err(e) = stream.send_buffer(&ConnectionInitData{ - prudpsock_addr: conn.socket_addr, - pid: conn.user_id - }.to_data()).await{ + if let Err(e) = stream + .send_buffer( + &ConnectionInitData { + prudpsock_addr: conn.socket_addr, + pid: conn.user_id, + } + .to_data() + .unwrap(), + ) + .await + { error!("error connecting to backend: {}", e); return; }; - - loop { tokio::select! { data = conn.recv() => { let Some(data) = data else { - break; + return; }; if let Err(e) = stream.send_buffer(&data[..]).await{ error!("error sending data to backend: {}", e); - break; + return; } }, data = stream.read_buffer() => { @@ -96,10 +96,10 @@ async fn main() { Ok(d) => d, Err(e) => { error!("error reveiving data from backend: {}", e); - break; + return; } }; - + if conn.send(data).await == None{ return; } @@ -111,4 +111,4 @@ async fn main() { } }); } -} \ 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..8446391 --- /dev/null +++ b/prudpv1/src/executables/proxy_secure.rs @@ -0,0 +1,102 @@ +use crate::executables::common::{EDGE_NODE_HOLDER, FORWARD_DESTINATION}; +use crate::prudp::router::Router; +use crate::prudp::secure::Secure; +use log::error; +use rnex_core::common::setup; +use rnex_core::executables::common::{ + OWN_IP_PRIVATE, OWN_IP_PUBLIC, SECURE_SERVER_ACCOUNT, SERVER_PORT, +}; +use rnex_core::prudp::virtual_port::VirtualPort; +use rnex_core::reggie::EdgeNodeHolderConnectOption::Register; +use rnex_core::reggie::RemoteEdgeNodeHolder; +use rnex_core::reggie::UnitPacketRead; +use rnex_core::reggie::UnitPacketWrite; +use rnex_core::rmc::protocols::{OnlyRemote, new_rmc_gateway_connection}; +use rnex_core::rmc::structures::RmcSerialize; +use rnex_core::rnex_proxy_common::ConnectionInitData; +use rnex_core::util::SplittableBufferConnection; +use std::net::SocketAddrV4; +use std::sync::Arc; +use std::time::Duration; +use tokio::net::TcpStream; +use tokio::task; +use tokio::time::sleep; + +pub async fn start() { + let (router_secure, _) = Router::new(SocketAddrV4::new(*OWN_IP_PRIVATE, *SERVER_PORT)) + .await + .expect("unable to start router"); + + let mut socket_secure = router_secure + .add_socket( + VirtualPort::new(1, 10), + Secure("6f599f81", SECURE_SERVER_ACCOUNT.clone()), + ) + .await + .expect("unable to add socket"); + + // let conn = socket_secure.connect(auth_sockaddr).await.unwrap(); + + loop { + let Some(mut conn) = socket_secure.accept().await else { + error!("server crashed"); + return; + }; + + task::spawn(async move { + let mut stream = match TcpStream::connect(*FORWARD_DESTINATION).await { + Ok(v) => v, + Err(e) => { + error!("unable to connect: {}", e); + return; + } + }; + + if let Err(e) = stream + .send_buffer( + &ConnectionInitData { + prudpsock_addr: conn.socket_addr, + pid: conn.user_id, + } + .to_data() + .unwrap(), + ) + .await + { + error!("error connecting to backend: {}", e); + return; + }; + + loop { + tokio::select! { + data = conn.recv() => { + let Some(data) = data else { + return; + }; + + if let Err(e) = stream.send_buffer(&data[..]).await{ + error!("error sending data to backend: {}", e); + return; + } + }, + data = stream.read_buffer() => { + let data = match data{ + Ok(d) => d, + Err(e) => { + error!("error reveiving data from backend: {}", e); + return; + } + }; + + if conn.send(data).await == None{ + return; + } + }, + _ = sleep(Duration::from_secs(10)) => { + conn.send([0,0,0,0,0].to_vec()).await; + } + } + } + }); + } +} 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..a4d87e9 100644 --- a/src/prudp/packet.rs +++ b/prudpv1/src/prudp/packet.rs @@ -3,20 +3,24 @@ // force the compiler to shut up here #![allow(unused_parens)] +use crate::prudp::packet::PacketOption::{ + ConnectionSignature, FragmentId, InitialSequenceId, MaximumSubstreamId, SupportedFunctions, +}; +use bytemuck::{Pod, Zeroable}; +use hmac::{Hmac, Mac}; +use log::{error, warn}; +use md5::{Digest, Md5}; +use rnex_core::prudp::socket_addr::PRUDPSockAddr; +use rnex_core::prudp::types_flags::TypesFlags; +use rnex_core::prudp::types_flags::flags::ACK; +use rnex_core::prudp::virtual_port::VirtualPort; use std::fmt::{Debug, Formatter}; use std::io; use std::io::{Cursor, Read, Seek, Write}; use std::net::SocketAddrV4; -use bytemuck::{Pod, Zeroable}; -use hmac::{Hmac, Mac}; -use log::{error, warn}; -use md5::{Md5, Digest}; use thiserror::Error; -use v_byte_macros::{SwapEndian}; -use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; -use crate::prudp::packet::flags::ACK; -use crate::prudp::packet::PacketOption::{ConnectionSignature, FragmentId, InitialSequenceId, MaximumSubstreamId, SupportedFunctions}; -use crate::prudp::sockaddr::PRUDPSockAddr; +use v_byte_helpers::SwapEndian; +use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; type Md5Hmac = Hmac; @@ -31,117 +35,11 @@ pub enum Error { #[error("invalid option id {0}")] InvalidOptionId(u8), #[error("option size {size} doesnt match expected option for given option id {id}")] - InvalidOptionSize { - id: u8, - size: u8, - }, + InvalidOptionSize { id: u8, size: u8 }, } pub type Result = 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 +57,7 @@ pub struct PRUDPV1Header { impl Default for PRUDPV1Header { fn default() -> Self { - Self{ + Self { magic: [0xEA, 0xD0], version: 1, session_id: 0, @@ -169,32 +67,30 @@ impl Default for PRUDPV1Header { destination_port: VirtualPort(0), types_and_flags: TypesFlags(0), packet_specific_size: 0, - substream_id: 0 + substream_id: 0, } } } - #[derive(Debug, Clone, Eq, PartialEq)] -pub enum PacketOption{ +pub enum PacketOption { SupportedFunctions(u32), ConnectionSignature([u8; 16]), FragmentId(u8), InitialSequenceId(u16), - MaximumSubstreamId(u8) + MaximumSubstreamId(u8), } -impl PacketOption{ - fn from(option_id: OptionId, option_data: &[u8]) -> io::Result{ - +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 +151,7 @@ impl OptionId { // Invariant is upheld because we only create the object if it doesn't violate the invariant match val { 0 | 1 | 2 | 3 | 4 => Ok(Self(val)), - _ => Err(Error::InvalidOptionId(val)) + _ => Err(Error::InvalidOptionId(val)), } } @@ -266,7 +162,7 @@ impl OptionId { 2 => 1, 3 => 2, 4 => 1, - _ => unreachable!() + _ => unreachable!(), } } } @@ -281,8 +177,7 @@ impl PRUDPV1Packet { pub fn new(reader: &mut (impl Read + Seek)) -> Result { let header: PRUDPV1Header = reader.read_struct(IS_BIG_ENDIAN)?; - if header.magic[0] != 0xEA || - header.magic[1] != 0xD0 { + if header.magic[0] != 0xEA || header.magic[1] != 0xD0 { return Err(Error::InvalidMagic(u16::from_be_bytes(header.magic))); } @@ -290,31 +185,31 @@ impl PRUDPV1Packet { return Err(Error::InvalidVersion(header.version)); } - let packet_signature: [u8; 16] = reader.read_struct(IS_BIG_ENDIAN)?; //let packet_signature: [u8; 16] = [0; 16]; assert_eq!(reader.stream_position().ok(), Some(14 + 16)); - - let mut packet_specific_buffer = vec![0u8; header.packet_specific_size as usize]; reader.read_exact(&mut packet_specific_buffer)?; - //no clue whats up with options but they are broken let mut packet_specific_data_cursor = Cursor::new(&packet_specific_buffer); let mut options = Vec::new(); loop { - let Ok(option_id): io::Result = 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 +229,21 @@ impl PRUDPV1Packet { } let mut option_data = vec![0u8; value_size as usize]; - if packet_specific_data_cursor.read_exact(&mut option_data[..]).is_err() { + if packet_specific_data_cursor + .read_exact(&mut option_data[..]) + .is_err() + { error!("unable to read options"); break; } options.push(PacketOption::from(option_id, &option_data)?); } - + let mut payload = vec![0u8; header.payload_size as usize]; reader.read_exact(&mut payload)?; - - Ok(Self { header, packet_signature, @@ -356,22 +252,21 @@ impl PRUDPV1Packet { }) } - pub fn base_acknowledgement_packet(&self) -> Self{ + pub fn base_acknowledgement_packet(&self) -> Self { let base = self.base_response_packet(); let mut flags = self.header.types_and_flags.flags(0); flags.set_flag(ACK); - let options = self.options + let options = self + .options .iter() .filter(|o| matches!(o, FragmentId(_))) .cloned() .collect(); - - - Self{ + Self { header: PRUDPV1Header { types_and_flags: flags, sequence_id: self.header.sequence_id, @@ -391,17 +286,24 @@ impl PRUDPV1Packet { } } - fn generate_options_bytes(&self) -> Vec{ + 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 +319,46 @@ impl PRUDPV1Packet { let mut hmac = Md5Hmac::new_from_slice(&key).expect("fuck"); - hmac.write(&header_data).expect("error during hmac calculation"); + hmac.write(&header_data) + .expect("error during hmac calculation"); if let Some(session_key) = session_key { - hmac.write(&session_key).expect("error during hmac calculation"); + hmac.write(&session_key) + .expect("error during hmac calculation"); } - hmac.write(&access_key_sum_bytes).expect("error during hmac calculation"); + hmac.write(&access_key_sum_bytes) + .expect("error during hmac calculation"); if let Some(connection_signature) = connection_signature { - hmac.write(&connection_signature).expect("error during hmac calculation"); + hmac.write(&connection_signature) + .expect("error during hmac calculation"); } - hmac.write(&option_bytes).expect("error during hmac calculation"); + hmac.write(&option_bytes) + .expect("error during hmac calculation"); - hmac.write_all(&self.payload).expect("error during hmac calculation"); + hmac.write_all(&self.payload) + .expect("error during hmac calculation"); - hmac.finalize().into_bytes()[0..16].try_into().expect("invalid hmac size") + hmac.finalize().into_bytes()[0..16] + .try_into() + .expect("invalid hmac size") } - pub fn calculate_and_assign_signature(&mut self, access_key: &str, session_key: Option<[u8; 32]>, connection_signature: Option<[u8; 16]>){ - self.packet_signature = self.calculate_signature_value(access_key, session_key, connection_signature); + pub fn calculate_and_assign_signature( + &mut self, + access_key: &str, + session_key: Option<[u8; 32]>, + connection_signature: Option<[u8; 16]>, + ) { + self.packet_signature = + self.calculate_signature_value(access_key, session_key, connection_signature); } - pub fn set_sizes(&mut self){ + pub fn set_sizes(&mut self) { self.header.packet_specific_size = self.options.iter().map(|o| o.write_size()).sum(); self.header.payload_size = self.payload.len() as u16; } - pub fn base_response_packet(&self) -> Self { + pub fn base_response_packet(&self) -> Self { Self { header: PRUDPV1Header { magic: [0xEA, 0xD0], @@ -455,19 +371,18 @@ impl PRUDPV1Packet { sequence_id: 0, session_id: 0, substream_id: 0, - }, packet_signature: [0; 16], payload: Default::default(), - options: Default::default() + options: Default::default(), } } - pub fn write_to(&self, writer: &mut impl Write) -> io::Result<()>{ + pub fn write_to(&self, writer: &mut impl Write) -> io::Result<()> { writer.write_all(bytemuck::bytes_of(&self.header))?; writer.write_all(&self.packet_signature)?; - for option in &self.options{ + for option in &self.options { option.write_to_stream(writer)?; } @@ -479,19 +394,24 @@ impl PRUDPV1Packet { #[cfg(test)] mod test { - use crate::prudp::packet::flags::{NEED_ACK, RELIABLE}; - use crate::prudp::packet::types::DATA; - use super::{OptionId, PacketOption, PRUDPV1Header, TypesFlags, VirtualPort}; + use super::{OptionId, PRUDPV1Header, PacketOption, TypesFlags}; + use rnex_core::prudp::{ + types_flags::{ + flags::{NEED_ACK, RELIABLE}, + types::DATA, + }, + virtual_port::VirtualPort, + }; #[test] fn size_test() { assert_eq!(size_of::(), 14); } #[test] - fn test_options(){ - let packet_types = [0,1,2,3,4]; + fn test_options() { + let packet_types = [0, 1, 2, 3, 4]; - for p_type in packet_types{ + for p_type in packet_types { let option_id = OptionId::new(p_type).unwrap(); let buf = vec![0; option_id.option_type_size() as usize]; @@ -505,12 +425,10 @@ mod test { assert_eq!(write_buf.len() as u8, opt.write_size()) } } - - } #[test] - fn header_read(){ + fn header_read() { let header = PRUDPV1Header { version: 0, destination_port: VirtualPort(0), @@ -520,8 +438,8 @@ mod test { packet_specific_size: 0, payload_size: 0, sequence_id: 0, - magic: [0xEA,0xD0], - source_port: VirtualPort(0) + magic: [0xEA, 0xD0], + source_port: VirtualPort(0), }; let bytes = bytemuck::bytes_of(&header); @@ -532,11 +450,11 @@ mod test { } #[test] - fn test_types_flags(){ + fn test_types_flags() { let types = TypesFlags::default().types(DATA).flags(NEED_ACK | RELIABLE); assert_ne!((types.0 >> 4) & NEED_ACK, 0); assert_ne!((types.0 >> 4) & RELIABLE, 0); assert_ne!((types.0 & 0xFF) as u8 & DATA, 0); } -} \ 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..69a4f9f --- /dev/null +++ b/rnex-core/Cargo.toml @@ -0,0 +1,53 @@ +[package] +name = "rnex-core" +version = "0.1.1" +edition = "2024" + +[dependencies] +bytemuck = { version = "1.21.0", features = ["derive"] } +dotenv = "0.15.0" +once_cell = "1.20.2" +rc4 = "0.1.0" +thiserror = "2.0.11" +v-byte-helpers = { git = "https://github.com/RusticMaple/VByteMacros", version = "0.1.1" } +simplelog = "0.12.2" +chrono = "0.4.39" +log = "0.4.25" +rand = "0.8.5" +cfg-if = "1.0.4" +hmac = "0.12.1" +md-5 = "^0.10.6" +tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread", "net", "sync", "fs"] } +hex = "0.4.3" + +macros = { path = "../macros" } +paste = "1.0.15" +typenum = "1.18.0" +json = "0.12.4" +anyhow = "1.0.100" +ureq = "3.1.4" + +[dev-dependencies] +# criterion = "0.7.0" + +[features] +rmc_struct_header = [] +guest_login = [] +friends = ["guest_login"] + +[[bench]] +name = "rmc_serialization" +harness = false + +[[bin]] +name = "backend_server_insecure" +path = "src/executables/backend_server_insecure.rs" + + +[[bin]] +name = "backend_server_secure" +path = "src/executables/backend_server_secure.rs" + +[[bin]] +name = "edge_node_holder_server" +path = "src/executables/edge_node_holder_server.rs" 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..51d9647 --- /dev/null +++ b/rnex-core/src/executables/backend_server_secure.rs @@ -0,0 +1,23 @@ +use cfg_if::cfg_if; +use rnex_core::common::setup; +use rnex_core::executables::common::new_simple_backend; +use rnex_core::executables::friends_backend::start_friends_backend; +use rnex_core::nex::matchmake::MatchmakeManager; +use rnex_core::nex::remote_console::RemoteConsole; +use rnex_core::nex::user::User; +use rnex_core::rmc::protocols::{RemoteDisconnectable, RmcPureRemoteObject}; +use std::sync::Arc; +use std::sync::atomic::AtomicU32; + +#[tokio::main] +async fn main() { + setup(); + + cfg_if! { + if #[cfg(feature = "friends")]{ + start_friends_backend().await; + } else { + regular_backend::start_regular_backend().await + } + } +} 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..2626fbe --- /dev/null +++ b/rnex-core/src/executables/friends_backend.rs @@ -0,0 +1,22 @@ +use std::sync::{Arc, atomic::AtomicU32}; + +use crate::{ + executables::common::new_simple_backend, + nex::friends_handler::{FriendsManager, FriendsUser}, +}; + +pub async fn start_friends_backend() { + let fm = Arc::new(FriendsManager { + cid_counter: AtomicU32::new(1), + }); + + new_simple_backend(move |c, r| { + let fm = fm.clone(); + Arc::new_cyclic(move |this| FriendsUser { + fm, + addr: c.prudpsock_addr, + pid: c.pid, + }) + }) + .await; +} 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 82% rename from src/grpc/account.rs rename to rnex-core/src/grpc/account.rs index d369501..a70149f 100644 --- a/src/grpc/account.rs +++ b/rnex-core/src/grpc/account.rs @@ -1,11 +1,10 @@ use std::{env, result}; use std::array::TryFromSliceError; -use std::str::FromStr; +use std::ops::Deref; use json::{object, JsonValue}; use once_cell::sync::Lazy; -use reqwest::{Body, Method, Url}; -use reqwest::header::HeaderValue; use thiserror::Error; +use tokio::task::{spawn_blocking, JoinError}; use crate::grpc::account::Error::SomethingHappened; static API_KEY: Lazy = Lazy::new(||{ let key = env::var("ACCOUNT_GQL_API_KEY") @@ -26,27 +25,38 @@ static CLIENT_URI: Lazy = Lazy::new(||{ #[derive(Error, Debug)] pub enum Error{ #[error(transparent)] - Creation(#[from] reqwest::Error), + RequestError(#[from] ureq::Error), #[error(transparent)] Json(#[from] json::Error), - #[error(transparent)] - Status(#[from] tonic::Status), + //#[error(transparent)] + //Status(#[from] tonic::Status), #[error("invalid password size: {0}")] PasswordConversion(#[from] TryFromSliceError), #[error("something happened")] - SomethingHappened + SomethingHappened, + #[error("error joining blocking task: {0}")] + Join(#[from] JoinError) } pub type Result = result::Result; -pub struct Client(reqwest::Client); +pub struct Client;//(reqwest::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{ + let request = ureq::post(CLIENT_URI.as_str()) + .header("X-API-Key", API_KEY.deref()) + .content_type("application/json"); + let mut response = spawn_blocking(move || request.send(request_data.to_string())).await??; + + let str_body = response.body_mut().read_to_string()?; + Ok(json::parse(&str_body)?) + /* let mut request = reqwest::Request::new(Method::POST, Url::from_str(CLIENT_URI.as_str()).unwrap()); *(request.body_mut()) = Some(Body::from(request_data.to_string())); @@ -56,6 +66,8 @@ impl Client{ let response = self.0.execute(request).await?; Ok(json::parse(&response.text().await?)?) + + */ } pub async fn get_nex_password(&mut self , pid: u32) -> Result<[u8; 16]>{ @@ -136,21 +148,4 @@ impl Client{ Ok(response) } } -*/ -#[cfg(test)] -mod test{ - use crate::grpc::account::Client; - - #[tokio::test] - async fn test(){ - dotenv::dotenv().ok(); - - let mut client = Client::new().await.unwrap(); - - let cli = client.get_nex_password(1699562916).await.unwrap(); - - println!("{:?}", cli); - } - - -} \ No newline at end of file +*/ \ 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/src/kerberos/mod.rs b/rnex-core/src/kerberos/mod.rs similarity index 50% rename from src/kerberos/mod.rs rename to rnex-core/src/kerberos/mod.rs index a40b816..4b56f56 100644 --- a/src/kerberos/mod.rs +++ b/rnex-core/src/kerberos/mod.rs @@ -1,23 +1,41 @@ -use std::io::{Read, Write}; -use bytemuck::{bytes_of, Pod, Zeroable}; +use bytemuck::{Pod, Zeroable, bytes_of}; +use cfg_if::cfg_if; use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc}; use hmac::Hmac; +use hmac::Mac; use md5::{Digest, Md5}; -use rc4::{Rc4, Rc4Core, StreamCipher}; +use rc4::KeyInit; use rc4::cipher::StreamCipherCoreWrapper; use rc4::consts::U16; -use hmac::Mac; -use rc4::KeyInit; -use crate::rmc::structures::RmcSerialize; +use rc4::{Rc4, Rc4Core, StreamCipher}; +use rnex_core::rmc::structures::RmcSerialize; +use std::io::{Read, Write}; +use typenum::Unsigned; + +use rnex_core::rmc::structures::Result; + +cfg_if! { + if #[cfg(feature = "friends")]{ + pub type SESSION_KEY_LENGTH_TY = U16; + } else { + pub type SESSION_KEY_LENGTH_TY = U32; + } +} +pub const SESSION_KEY_LENGTH: usize = SESSION_KEY_LENGTH_TY::USIZE; type Md5Hmac = Hmac; -pub fn derive_key(pid: u32, password: [u8; 16]) -> [u8; 16]{ - let iteration_count = 65000 + pid%1024; +pub fn derive_key(pid: u32, password: &[u8]) -> [u8; 16] { + let iteration_count = 65000 + pid % 1024; + // we do one iteration out here to ensure the key is always 16 bytes - let mut key = password; + let mut key: [u8; 16] = { + let mut md5 = Md5::new(); + md5.update(password); + md5.finalize().try_into().unwrap() + }; - for _ in 0..iteration_count { + for _ in 1..iteration_count { let mut md5 = Md5::new(); md5.update(key); key = md5.finalize().try_into().unwrap(); @@ -29,12 +47,12 @@ pub fn derive_key(pid: u32, password: [u8; 16]) -> [u8; 16]{ #[repr(transparent)] pub struct KerberosDateTime(pub u64); -impl KerberosDateTime{ - pub fn new(second: u64, minute: u64, hour: u64, day: u64, month: u64, year:u64 ) -> Self { +impl KerberosDateTime { + pub fn new(second: u64, minute: u64, hour: u64, day: u64, month: u64, year: u64) -> Self { Self(second | (minute << 6) | (hour << 12) | (day << 17) | (month << 22) | (year << 26)) } - pub fn now() -> Self{ + pub fn now() -> Self { let now = chrono::Utc::now(); Self::new( now.second() as u64, @@ -47,69 +65,80 @@ impl KerberosDateTime{ } #[inline] - pub fn get_seconds(&self) -> u8{ + pub fn get_seconds(&self) -> u8 { (self.0 & 0b111111) as u8 } #[inline] - pub fn get_minutes(&self) -> u8{ + pub fn get_minutes(&self) -> u8 { ((self.0 >> 6) & 0b111111) as u8 } #[inline] - pub fn get_hours(&self) -> u8{ + pub fn get_hours(&self) -> u8 { ((self.0 >> 12) & 0b111111) as u8 } #[inline] - pub fn get_days(&self) -> u8{ + pub fn get_days(&self) -> u8 { ((self.0 >> 17) & 0b111111) as u8 } #[inline] - pub fn get_month(&self) -> u8{ + pub fn get_month(&self) -> u8 { ((self.0 >> 22) & 0b1111) as u8 } #[inline] - pub fn get_year(&self) -> u64{ + pub fn get_year(&self) -> u64 { (self.0 >> 26) & 0xFFFFFFFF } - pub fn to_regular_time(&self) -> chrono::DateTime{ + 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() + NaiveDate::from_ymd_opt( + self.get_year() as i32, + self.get_month() as u32, + self.get_days() as u32, + ) + .unwrap(), + NaiveTime::from_hms_opt( + self.get_hours() as u32, + self.get_minutes() as u32, + self.get_seconds() as u32, + ) + .unwrap(), + ) + .and_utc() } } -impl RmcSerialize for KerberosDateTime{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { +impl RmcSerialize for KerberosDateTime { + fn serialize(&self, writer: &mut impl Write) -> Result<()> { Ok(self.0.serialize(writer)?) } - fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { + fn deserialize(reader: &mut impl Read) -> Result { Ok(Self(u64::deserialize(reader)?)) } } #[derive(Pod, Zeroable, Copy, Clone)] #[repr(C, packed)] -pub struct TicketInternalData{ +pub struct TicketInternalData { pub issued_time: KerberosDateTime, pub pid: u32, - pub session_key: [u8; 32], + pub session_key: [u8; SESSION_KEY_LENGTH], } -impl TicketInternalData{ - pub(crate) fn new(pid: u32) -> Self{ - Self{ +impl TicketInternalData { + pub(crate) fn new(pid: u32) -> Self { + Self { issued_time: KerberosDateTime::now(), pid, - session_key: rand::random() + session_key: rand::random(), } } - pub(crate) fn encrypt(&self, key: [u8; 16]) -> Box<[u8]>{ + pub(crate) fn encrypt(&self, key: [u8; 16]) -> Box<[u8]> { let mut data = bytes_of(self).to_vec(); let mut rc4: StreamCipherCoreWrapper> = Rc4::new_from_slice(&key).unwrap(); @@ -117,11 +146,13 @@ impl TicketInternalData{ let mut hmac = ::new_from_slice(&key).unwrap(); - hmac.write_all(&data[..]).expect("failed to write data to hmac"); + hmac.write_all(&data[..]) + .expect("failed to write data to hmac"); let hmac_result = &hmac.finalize().into_bytes()[..]; - data.write_all(&hmac_result).expect("failed to write data to vec"); + data.write_all(&hmac_result) + .expect("failed to write data to vec"); data.into_boxed_slice() } @@ -129,42 +160,44 @@ impl TicketInternalData{ #[derive(Pod, Zeroable, Copy, Clone)] #[repr(C, packed)] -pub struct Ticket{ - pub session_key: [u8; 32], +pub struct Ticket { + pub session_key: [u8; SESSION_KEY_LENGTH], pub pid: u32, } -impl Ticket{ - pub(crate) fn encrypt(&self, key: [u8; 16], internal_data: &[u8]) -> Box<[u8]>{ +impl Ticket { + pub fn encrypt(&self, key: [u8; 16], internal_data: &[u8]) -> Box<[u8]> { let mut data = bytes_of(self).to_vec(); - internal_data.serialize(&mut data).expect("unable to write to vec"); + internal_data + .serialize(&mut data) + .expect("unable to write to vec"); let mut rc4: StreamCipherCoreWrapper> = 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"); + hmac.write_all(&data[..]) + .expect("failed to write data to hmac"); let hmac_result = &hmac.finalize().into_bytes()[..]; - data.write_all(&hmac_result).expect("failed to write data to vec"); + data.write_all(&hmac_result) + .expect("failed to write data to vec"); data.into_boxed_slice() } } - #[cfg(test)] -mod test{ - use chrono::{Datelike, Utc}; +mod test { use crate::kerberos::KerberosDateTime; #[test] - fn kerberos_time_convert_test(){ + fn kerberos_time_convert_test() { let time = KerberosDateTime(135904948834); println!("{}", time.to_regular_time().to_rfc2822()); } -} \ No newline at end of file +} diff --git a/src/lib.rs b/rnex-core/src/lib.rs similarity index 74% rename from src/lib.rs rename to rnex-core/src/lib.rs index 53553c2..3920565 100644 --- a/src/lib.rs +++ b/rnex-core/src/lib.rs @@ -6,9 +6,8 @@ -extern crate self as rust_nex; +extern crate self as rnex_core; -pub mod endianness; pub mod prudp; pub mod rmc; //mod protocols; @@ -18,9 +17,13 @@ pub mod kerberos; pub mod nex; pub mod result; pub mod versions; -pub mod web; pub mod common; pub mod reggie; pub mod rnex_proxy_common; pub mod util; pub mod executables; +pub use macros::*; + +pub mod config{ + pub const FEATURE_HAS_STRUCT_HEADER: bool = cfg!(feature = "rmc_struct_header"); +} 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..b3a5bb6 --- /dev/null +++ b/rnex-core/src/nex/account.rs @@ -0,0 +1,32 @@ +use macros::RmcSerialize; + +#[derive(RmcSerialize, Clone)] +pub struct Account { + pub pid: u32, + pub username: String, + pub kerbros_password: Box<[u8]>, +} + +impl Account { + pub fn new(pid: u32, username: &str, passwd: &str) -> Self { + let passwd_data = passwd.as_bytes(); + + Self { + kerbros_password: passwd.as_bytes().into(), + username: username.into(), + pid, + } + } + + pub fn new_raw_password(pid: u32, username: &str, passwd: &[u8]) -> Self { + Self { + kerbros_password: passwd.into(), + username: username.into(), + pid, + } + } + + pub fn get_login_data(&self) -> (u32, &[u8]) { + (self.pid, &self.kerbros_password) + } +} diff --git a/src/nex/auth_handler.rs b/rnex-core/src/nex/auth_handler.rs similarity index 53% rename from src/nex/auth_handler.rs rename to rnex-core/src/nex/auth_handler.rs index ca7f970..0ab6910 100644 --- a/src/nex/auth_handler.rs +++ b/rnex-core/src/nex/auth_handler.rs @@ -1,19 +1,22 @@ +use crate::grpc::account; +use crate::reggie::{RemoteEdgeNodeHolder, RemoteEdgeNodeManagement}; +use crate::{define_rmc_proto, kerberos}; +use cfg_if::cfg_if; +use log::{info, warn}; +use macros::rmc_struct; +use rnex_core::kerberos::{KerberosDateTime, Ticket, derive_key}; +use rnex_core::nex::account::Account; +use rnex_core::rmc::protocols::OnlyRemote; +use rnex_core::rmc::protocols::auth::{Auth, RawAuth, RawAuthInfo, RemoteAuth}; +use rnex_core::rmc::response::ErrorCode; +use rnex_core::rmc::response::ErrorCode::Core_Unknown; +use rnex_core::rmc::structures::any::Any; +use rnex_core::rmc::structures::connection_data::ConnectionData; +use rnex_core::rmc::structures::connection_data::ConnectionDataOld; +use rnex_core::rmc::structures::qresult::QResult; use std::hash::{DefaultHasher, Hasher}; use std::net::SocketAddrV4; -use std::sync::Arc; -use crate::grpc::account; -use crate::kerberos::{derive_key, KerberosDateTime, Ticket}; -use crate::nex::account::Account; -use crate::rmc::protocols::auth::{Auth, RawAuth, RawAuthInfo, RemoteAuth}; -use crate::rmc::response::ErrorCode; -use crate::rmc::response::ErrorCode::Core_Unknown; -use crate::rmc::structures::any::Any; -use crate::rmc::structures::connection_data::ConnectionData; -use crate::rmc::structures::qresult::QResult; -use crate::{define_rmc_proto, kerberos}; -use macros::rmc_struct; -use crate::reggie::{RemoteEdgeNodeHolder, RemoteEdgeNodeManagement}; -use crate::rmc::protocols::OnlyRemote; +use std::sync::{Arc, LazyLock, OnceLock}; define_rmc_proto!( proto AuthClientProtocol{ @@ -30,8 +33,8 @@ pub struct AuthHandler { } pub fn generate_ticket( - source_act_login_data: (u32, [u8; 16]), - dest_act_login_data: (u32, [u8; 16]), + source_act_login_data: (u32, &[u8]), + dest_act_login_data: (u32, &[u8]), ) -> Box<[u8]> { let source_key = derive_key(source_act_login_data.0, source_act_login_data.1); let dest_key = derive_key(dest_act_login_data.0, dest_act_login_data.1); @@ -48,7 +51,13 @@ pub fn generate_ticket( encrypted_session_ticket } -async fn get_login_data_by_pid(pid: u32) -> Option<(u32, [u8; 16])> { +async fn get_login_data_by_pid(pid: u32) -> Option<(u32, Box<[u8]>)> { + if pid == GUEST_ACCOUNT.pid { + let source_login_data = GUEST_ACCOUNT.get_login_data(); + + return Some((source_login_data.0, source_login_data.1.into())); + } + let Ok(mut client) = account::Client::new().await else { return None; }; @@ -57,19 +66,96 @@ async fn get_login_data_by_pid(pid: u32) -> Option<(u32, [u8; 16])> { return None; }; - Some((pid, passwd)) + Some((pid, passwd.into())) } -fn station_url_from_sock_addr(sock_addr: SocketAddrV4) -> String{ +fn station_url_from_sock_addr(sock_addr: SocketAddrV4) -> String { format!( "prudps:/PID=2;sid=1;stream=10;type=2;address={};port={};CID=1", - sock_addr.ip(), sock_addr.port() + sock_addr.ip(), + sock_addr.port() ) } +static GUEST_ACCOUNT: LazyLock = + LazyLock::new(|| Account::new(100, "guest", "MMQea3n!fsik")); + +impl AuthHandler { + pub async fn generate_ticket_from_name( + &self, + name: &str, + ) -> Result<(u32, Box<[u8]>), ErrorCode> { + #[cfg(feature = "guest_login")] + { + if name == GUEST_ACCOUNT.username { + let source_login_data = GUEST_ACCOUNT.get_login_data(); + let destination_login_data = self.destination_server_acct.get_login_data(); + + return Ok(( + source_login_data.0, + generate_ticket(source_login_data, destination_login_data), + )); + } + } + let Ok(pid) = name.parse() else { + warn!("unable to connect to parse pid: {}", name); + return Err(ErrorCode::Core_InvalidArgument); + }; + + let Ok(mut client) = account::Client::new().await else { + warn!("unable to connect to grpc"); + return Err(ErrorCode::Core_Exception); + }; + + let Ok(passwd) = client.get_nex_password(pid).await else { + warn!("unable to get nex password"); + return Err(ErrorCode::Core_Exception); + }; + + let source_login_data = (pid, &passwd[..]); + let destination_login_data = self.destination_server_acct.get_login_data(); + + Ok(( + pid, + generate_ticket(source_login_data, destination_login_data), + )) + } +} + impl Auth for AuthHandler { - async fn login(&self, _name: String) -> Result<(), ErrorCode> { - todo!() + async fn login( + &self, + name: String, + ) -> Result<(QResult, u32, Vec, ConnectionDataOld, String), ErrorCode> { + let (pid, ticket) = self.generate_ticket_from_name(&name).await?; + + let result = QResult::success(Core_Unknown); + + let mut hasher = DefaultHasher::new(); + + hasher.write(name.as_bytes()); + + let Ok(addr) = self.control_server.get_url(hasher.finish()).await else { + warn!("no secure proxies"); + return Err(ErrorCode::Core_Exception); + }; + + let connection_data = ConnectionDataOld { + station_url: station_url_from_sock_addr(addr), + special_station_url: "".to_string(), + special_protocols: Vec::new(), + }; + + let ret = ( + result, + pid, + ticket.into(), + connection_data, + self.build_name.to_string(), + ); + + info!("data: {:?}", ret); + Ok(ret) } async fn login_ex( @@ -77,30 +163,16 @@ impl Auth for AuthHandler { name: String, _extra_data: Any, ) -> Result<(QResult, u32, Vec, ConnectionData, String), ErrorCode> { - let Ok(pid) = name.parse() else { - return Err(ErrorCode::Core_InvalidArgument); - }; - - let Ok(mut client) = account::Client::new().await else { - return Err(ErrorCode::Core_Exception); - }; - - let Ok(passwd) = client.get_nex_password(pid).await else { - return Err(ErrorCode::Core_Exception); - }; - - let source_login_data = (pid, passwd); - let destination_login_data = self.destination_server_acct.get_login_data(); - - let ticket = generate_ticket(source_login_data, destination_login_data); + let (pid, ticket) = self.generate_ticket_from_name(&name).await?; let result = QResult::success(Core_Unknown); let mut hasher = DefaultHasher::new(); hasher.write(name.as_bytes()); - + let Ok(addr) = self.control_server.get_url(hasher.finish()).await else { + warn!("no secure proxies"); return Err(ErrorCode::Core_Exception); }; @@ -112,13 +184,16 @@ impl Auth for AuthHandler { special_protocols: Vec::new(), }; - Ok(( + let ret = ( result, - source_login_data.0, + pid, ticket.into(), connection_data, - self.build_name.to_string() //format!("{}; Rust NEX Version {} by DJMrTV", self.build_name, env!("CARGO_PKG_VERSION")), - )) + self.build_name.to_string(), + ); + + info!("data: {:?}", ret); + Ok(ret) } async fn request_ticket( @@ -126,22 +201,19 @@ impl Auth for AuthHandler { source_pid: u32, destination_pid: u32, ) -> Result<(QResult, Vec), ErrorCode> { - let Some(source_login_data) = get_login_data_by_pid(source_pid).await else { + let Some((pid, passwd)) = get_login_data_by_pid(source_pid).await else { return Err(ErrorCode::Core_Exception); }; let desgination_login_data = if destination_pid == self.destination_server_acct.pid { self.destination_server_acct.get_login_data() } else { - let Some(login) = get_login_data_by_pid(destination_pid).await else { - return Err(ErrorCode::Core_Exception); - }; - login + return Err(ErrorCode::RendezVous_InvalidOperation); }; let result = QResult::success(Core_Unknown); - let ticket = generate_ticket(source_login_data, desgination_login_data); + let ticket = generate_ticket((pid, &passwd[..]), desgination_login_data); Ok((result, ticket.into())) } @@ -157,30 +229,30 @@ impl Auth for AuthHandler { #[cfg(test)] mod test { - use crate::rmc::structures::connection_data::ConnectionData; - use crate::rmc::structures::qresult::QResult; - use crate::rmc::structures::RmcSerialize; - use crate::rmc::response::RMCResponse; + use rnex_core::rmc::response::RMCResponse; + use rnex_core::rmc::structures::RmcSerialize; + use rnex_core::rmc::structures::connection_data::ConnectionData; + use rnex_core::rmc::structures::qresult::QResult; use std::io::Cursor; - + #[test] fn test() { - + return; let stuff = hex::decode("200100000a0106000000028000000100010051b3995774000000a6321c7f78847c1c5e9fb825eb26bd91841f1a40d92fc694159666119cb13527f1463ac48ad42a63e6613ede67041554b1770978112e6f1f3e177a2bfc75933216dbe38f70133a1eb28e2ae32a4b5c4b0c3e3efd4c02907992e259b257270b57a9dbe7792f4721b07f8fafb9e32d50f2555c616a015c0000004b007072756470733a2f5049443d323b7369643d313b73747265616d3d31303b747970653d323b616464726573733d322e3234332e39352e3131333b706f72743d31303030313b4349443d3100000000000100002c153ba51f00000033006272616e63683a6f726967696e2f70726f6a6563742f7775702d61676d6a206275696c643a335f385f31355f323030345f3000").unwrap(); let stuff = RMCResponse::new(&mut Cursor::new(stuff)).unwrap(); - let crate::rmc::response::RMCResponseResult::Success { call_id, method_id, data: stuff} = stuff.response_result else { + let rnex_core::rmc::response::RMCResponseResult::Success { .. } = stuff.response_result + else { panic!() }; - - // let stuff = hex::decode("0100010051B399577400000085F1736FCFBE93660275A3FE36FED6C2EFC57222AC99A9219CF54170A415B02DF1463AC48AD42A6307813FDE67041554B177097832ED000F892D9551A09F88E9CB0388DC1BC9527CC7384556A3287B2A349ABBF7E34A5A3EC14C2287CC7F78DA616BC3B03A035347FBD2E9A505C8EF42447CD809015F0000004E007072756470733A2F73747265616D3D31303B747970653D323B616464726573733D3139322E3136382E3137382E3132303B706F72743D31303030313B4349443D313B5049443D323B7369643D310000000000010000CDF53AA51F00000033006272616E63683A6F726967696E2F70726F6A6563742F7775702D61676D6A206275696C643A335F385F31355F323030345F3000").unwrap(); - // let stuff = hex::decode("0100010051b399577400000037d3d4814d2b16dd546c94a75d32637b45f856b5abe73cf26cfaa235c5f2c1cef1463ac48ad42a637d873fde67041554b177097880cfa7e10bb810eaf686bfb0a0cf3d65b1f476ebc046d0855327986f557dca14fbb8594883c186b863f2206f22baa0309dbcc81da2f883cb2cdc12628ec7fced015c0000004b007072756470733a2f5049443d323b7369643d313b73747265616d3d31303b747970653d323b616464726573733d322e3234332e39352e3131333b706f72743d31303030313b4349443d310000000000010000b7f33aa51f00000033006272616e63683a6f726967696e2f70726f6a6563742f7775702d61676d6a206275696c643a335f385f31355f323030345f3000").unwrap(); + let stuff = hex::decode("0100010051b399577400000037d3d4814d2b16dd546c94a75d32637b45f856b5abe73cf26cfaa235c5f2c1cef1463ac48ad42a637d873fde67041554b177097880cfa7e10bb810eaf686bfb0a0cf3d65b1f476ebc046d0855327986f557dca14fbb8594883c186b863f2206f22baa0309dbcc81da2f883cb2cdc12628ec7fced015c0000004b007072756470733a2f5049443d323b7369643d313b73747265616d3d31303b747970653d323b616464726573733d322e3234332e39352e3131333b706f72743d31303030313b4349443d310000000000010000b7f33aa51f00000033006272616e63683a6f726967696e2f70726f6a6563742f7775702d61676d6a206275696c643a335f385f31355f323030345f3000").unwrap(); let data = <(QResult, u32, Vec, ConnectionData, String) as RmcSerialize>::deserialize( &mut Cursor::new(stuff), - ).unwrap(); + ) + .unwrap(); println!("data: {:?}", data); } diff --git a/rnex-core/src/nex/common.rs b/rnex-core/src/nex/common.rs new file mode 100644 index 0000000..d2fa40f --- /dev/null +++ b/rnex-core/src/nex/common.rs @@ -0,0 +1,93 @@ +use rnex_core::prudp::station_url::StationUrl; +use rnex_core::prudp::station_url::UrlOptions::{ + Address, NatFiltering, NatMapping, NatType, Port, PrincipalID, RVConnectionID, +}; +use rnex_core::prudp::station_url::nat_types::PUBLIC; +use rnex_core::rmc::response::ErrorCode::Core_Exception; + +use rnex_core::prudp::socket_addr::PRUDPSockAddr; +use rnex_core::rmc::response::ErrorCode; + +pub async fn get_station_urls( + station_urls: &[StationUrl], + addr: PRUDPSockAddr, + pid: u32, + cid: u32, +) -> Result, 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())); + 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..a2d42ca --- /dev/null +++ b/rnex-core/src/nex/friends_handler.rs @@ -0,0 +1,160 @@ +use std::sync::{Arc, atomic::AtomicU32}; +use std::time::Duration; + +use log::info; +use macros::rmc_struct; +use rnex_core::rmc::protocols::friends::{Friends, RawFriends, RawFriendsInfo, RemoteFriends}; +use rnex_core::rmc::protocols::secure::{RawSecure, RawSecureInfo, RemoteSecure, Secure}; +use rnex_core::{ + define_rmc_proto, + kerberos::KerberosDateTime, + nex::common::get_station_urls, + prudp::{socket_addr::PRUDPSockAddr, station_url::StationUrl}, + rmc::{ + protocols::friends::{ + BlacklistedPrincipal, Comment, FriendInfo, FriendRequest, NNAInfo, NintendoPresenceV2, + PersistentNotification, PrincipalPreference, + }, + response::ErrorCode, + structures::{any::Any, qresult::QResult}, + }, +}; +use std::sync::atomic::Ordering::Relaxed; +use tokio::time::sleep; + +use rnex_core::rmc::protocols::friends::{GameKey, MiiV2, PrincipalBasicInfo}; + +define_rmc_proto!( + proto FriendsUser{ + Secure, + Friends + } +); +#[rmc_struct(FriendsUser)] +pub struct FriendsUser { + pub fm: Arc, + pub addr: PRUDPSockAddr, + pub pid: u32, +} + +pub struct FriendsManager { + pub cid_counter: AtomicU32, +} + +impl FriendsManager { + pub fn next_cid(&self) -> u32 { + self.cid_counter.fetch_add(1, Relaxed) + } +} + +impl Friends for FriendsUser { + async fn update_and_get_all_information( + &self, + info: NNAInfo, + presence: NintendoPresenceV2, + date_time: KerberosDateTime, + ) -> Result< + ( + PrincipalPreference, + Comment, + Vec, + Vec, + Vec, + Vec, + bool, + Vec, + bool, + ), + ErrorCode, + > { + Ok(( + PrincipalPreference { + block_friend_request: false, + show_online: false, + show_playing_title: false, + }, + Comment { + last_changed: KerberosDateTime::now(), + message: "".to_string(), + unk: 0, + }, + vec![FriendInfo { + became_friends: KerberosDateTime::now(), + comment: Comment { + last_changed: KerberosDateTime::now(), + message: "I'm just a dummy account :3".to_string(), + unk: 0, + }, + last_online: KerberosDateTime::now(), + nna_info: NNAInfo { + principal_basic_info: PrincipalBasicInfo { + pid: 101, + nnid: "dummyaccount".to_string(), + mii: MiiV2{ + date_time: KerberosDateTime::now(), + name: "Dummy Account".to_string(), + mii_data: hex::decode("030000402bd7c32986a771f2dc6b35e31da15e37ff7c0000391e6f006f006d0069000000000000000000000000004040001065033568641e2013661a611821640f0000290052485000000000000000000000000000000000000000000000e838").unwrap(), + unk: 0, + unk2: 0, + }, + unk: 0 + }, + unk: 0, + unk2: 0 + }, + presence: NintendoPresenceV2{ + changed_flags: 0, + message: "".to_string(), + app_data: vec![], + game_key: GameKey{ + tid: 0x00050002101ce400, + version: 0x0 + }, + game_server_id: 0, + is_online: true, + gid: 0, + pid: 101, + unk: 0, + unk2: 0, + unk3: 0, + unk4: 0, + unk5: 0, + unk6: 0, + unk7: 0 + }, + unk: 0 + }], + vec![], + vec![], + vec![], + false, + vec![], + false, + )) + } +} + +impl Secure for FriendsUser { + async fn register( + &self, + station_urls: Vec, + ) -> 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) + } +} diff --git a/rnex-core/src/nex/matchmake.rs b/rnex-core/src/nex/matchmake.rs new file mode 100644 index 0000000..732c1ea --- /dev/null +++ b/rnex-core/src/nex/matchmake.rs @@ -0,0 +1,481 @@ +use crate::nex::user::User; +use crate::rmc::protocols::notifications::{NotificationEvent, RemoteNotification}; +use log::info; +use rand::random; +use rnex_core::kerberos::KerberosDateTime; +use rnex_core::rmc::protocols::notifications::notification_types::{ + HOST_CHANGED, OWNERSHIP_CHANGED, +}; +use rnex_core::rmc::response::ErrorCode; +use rnex_core::rmc::response::ErrorCode::{Core_InvalidArgument, RendezVous_SessionVoid}; +use rnex_core::rmc::structures::matchmake::gathering_flags::PERSISTENT_GATHERING; +use rnex_core::rmc::structures::matchmake::{ + Gathering, MatchmakeParam, MatchmakeSession, MatchmakeSessionSearchCriteria, +}; +use rnex_core::rmc::structures::variant::Variant; +use std::collections::HashMap; +use std::str::FromStr; +use std::sync::atomic::AtomicU32; +use std::sync::atomic::Ordering::Relaxed; +use std::sync::{Arc, Weak}; +use std::time::Duration; +use tokio::sync::{Mutex, RwLock}; +use tokio::time::sleep; + +pub struct MatchmakeManager { + //pub gid_counter: AtomicU32, + pub sessions: RwLock>>>, + 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, + param_2: other_pid, + str_param: "".into(), + param_3: 0, + }) + .await; + } + + let list_of_connected_pids: Vec<_> = self + .connected_players + .iter() + .filter_map(|p| p.upgrade()) + .map(|p| p.pid) + .collect(); + + for other_connection in conns { + let Some(other_conn) = other_connection.upgrade() else { + continue; + }; + + // let other_pid = other_conn.pid; + /*if other_pid == self.session.gathering.owner_pid && + joining_pid == self.session.gathering.owner_pid{ + continue; + }*/ + + for pid in &list_of_connected_pids { + other_conn + .remote + .process_notification_event(NotificationEvent { + pid_source: initiating_pid, + notif_type: 3001, + param_1: self.session.gathering.self_gid, + param_2: *pid, + str_param: join_msg.clone(), + param_3: self.connected_players.len() as _, + }) + .await; + } + } + + for old_conns in &old_particip { + let Some(old_conns) = old_conns.upgrade() else { + continue; + }; + + let older_pid = old_conns.pid; + + initiating_user + .remote + .process_notification_event(NotificationEvent { + pid_source: initiating_pid, + notif_type: 3001, + param_1: self.session.gathering.self_gid, + param_2: older_pid, + str_param: join_msg.clone(), + param_3: self.connected_players.len() as _, + }) + .await; + } + } + pub fn has_active_players(&self) -> bool { + self.connected_players + .iter() + .filter(|v| v.upgrade().is_some()) + .count() + != 0 + } + + #[inline] + pub fn is_reachable(&self) -> bool { + (if self.session.gathering.flags & PERSISTENT_GATHERING != 0 { + if self.has_active_players() { + true + } else { + self.session.open_participation + } + } else { + self.has_active_players() + }) & self.has_active_players() + } + #[inline] + pub fn is_joinable(&self) -> bool { + self.is_reachable() && self.session.open_participation + } + + pub fn matches_criteria( + &self, + search_criteria: &MatchmakeSessionSearchCriteria, + ) -> Result { + // 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(()) + } +} 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/src/nex/user.rs b/rnex-core/src/nex/user.rs similarity index 59% rename from src/nex/user.rs rename to rnex-core/src/nex/user.rs index e166a80..1666d56 100644 --- a/src/nex/user.rs +++ b/rnex-core/src/nex/user.rs @@ -1,33 +1,43 @@ use crate::define_rmc_proto; +use crate::nex::common::get_station_urls; use crate::nex::matchmake::{ExtendedMatchmakeSession, MatchmakeManager}; use crate::nex::remote_console::RemoteConsole; -use crate::prudp::sockaddr::PRUDPSockAddr; -use crate::prudp::station_url::UrlOptions::{ - Address, NatFiltering, NatMapping, NatType, Port, PrincipalID, RVConnectionID, - -}; -use crate::prudp::station_url::{StationUrl}; use crate::rmc::protocols::matchmake::{ Matchmake, RawMatchmake, RawMatchmakeInfo, RemoteMatchmake, }; -use crate::rmc::protocols::ranking::{Ranking, RawRanking, RawRankingInfo, RemoteRanking}; -use crate::rmc::protocols::matchmake_extension::{ +use crate::rmc::protocols::nat_traversal::{ + NatTraversal, RawNatTraversal, RawNatTraversalInfo, RemoteNatTraversal, + RemoteNatTraversalConsole, +}; +use rnex_core::prudp::station_url::StationUrl; +use rnex_core::prudp::station_url::UrlOptions::{ + Address, NatFiltering, NatMapping, NatType, Port, PrincipalID, RVConnectionID, +}; +use rnex_core::rmc::protocols::matchmake_ext::{ + MatchmakeExt, RawMatchmakeExt, RawMatchmakeExtInfo, RemoteMatchmakeExt, +}; +use rnex_core::rmc::protocols::matchmake_extension::{ MatchmakeExtension, RawMatchmakeExtension, RawMatchmakeExtensionInfo, RemoteMatchmakeExtension, }; -use crate::rmc::protocols::nat_traversal::{NatTraversal, RawNatTraversal, RawNatTraversalInfo, RemoteNatTraversal, RemoteNatTraversalConsole}; -use crate::rmc::protocols::secure::{RawSecure, RawSecureInfo, RemoteSecure, Secure}; -use crate::rmc::protocols::matchmake_ext::{MatchmakeExt, RawMatchmakeExt, RawMatchmakeExtInfo, RemoteMatchmakeExt}; -use crate::rmc::response::ErrorCode; -use crate::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession}; +use rnex_core::rmc::protocols::ranking::{Ranking, RawRanking, RawRankingInfo, RemoteRanking}; +use rnex_core::rmc::protocols::secure::{RawSecure, RawSecureInfo, RemoteSecure, Secure}; +use rnex_core::rmc::response::ErrorCode; +use rnex_core::rmc::structures::any::Any; +use rnex_core::rmc::structures::matchmake::{ + AutoMatchmakeParam, CreateMatchmakeSessionParam, JoinMatchmakeSessionParam, MatchmakeSession, +}; -use crate::rmc::structures::qresult::QResult; -use macros::rmc_struct; -use std::sync::{Arc, Weak}; -use log::info; -use tokio::sync::{Mutex, RwLock}; -use crate::prudp::station_url::nat_types::PUBLIC; use crate::rmc::protocols::notifications::{NotificationEvent, RemoteNotification}; -use crate::rmc::response::ErrorCode::{Core_Exception, Core_InvalidArgument, RendezVous_AccountExpired}; +use log::info; +use macros::rmc_struct; +use rnex_core::prudp::socket_addr::PRUDPSockAddr; +use rnex_core::prudp::station_url::nat_types::PUBLIC; +use rnex_core::rmc::response::ErrorCode::{ + Core_Exception, Core_InvalidArgument, RendezVous_AccountExpired, +}; +use rnex_core::rmc::structures::qresult::QResult; +use std::sync::{Arc, Weak}; +use tokio::sync::{Mutex, RwLock}; define_rmc_proto!( proto UserProtocol{ @@ -63,98 +73,27 @@ impl Secure for User { users.insert(cid, self.this.clone()); drop(users); - let mut public_station: Option = 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 stations = get_station_urls(&station_urls, self.ip, self.pid, cid).await?; + let first = stations.first().unwrap().clone(); let mut lock = self.station_url.write().await; - *lock = vec![ - public_station.clone(), - // private_station.clone() - ]; + *lock = stations; drop(lock); let result = QResult::success(ErrorCode::Core_Unknown); - let out = public_station.to_string(); + Ok((result, cid, first)) + } - println!("out: {}", out); - - Ok((result, cid, public_station)) + async fn register_ex( + &self, + station_urls: Vec, + _data: Any, + ) -> Result<(QResult, u32, StationUrl), ErrorCode> { + self.register(station_urls).await } async fn replace_url(&self, target_url: StationUrl, dest: StationUrl) -> Result<(), ErrorCode> { @@ -169,8 +108,8 @@ impl Secure for User { }; let Some(replacement_target) = lock.iter_mut().find(|url| { - url.options.iter().any(|o| o == target_addr) && - url.options.iter().any(|o| o == target_port) + url.options.iter().any(|o| o == target_addr) + && url.options.iter().any(|o| o == target_port) }) else { return Err(ErrorCode::Core_InvalidArgument); }; @@ -230,22 +169,34 @@ impl MatchmakeExtension for User { create_session_param.matchmake_session, &self.this.clone(), ) - .await; + .await; let mut joining_players = vec![self.this.clone()]; let users = self.matchmake_manager.users.read().await; - if let Ok(old_gathering) = self.matchmake_manager.get_session(create_session_param.gid_for_participation_check).await { + if let Ok(old_gathering) = self + .matchmake_manager + .get_session(create_session_param.gid_for_participation_check) + .await + { let old_gathering = old_gathering.lock().await; - let players = old_gathering.connected_players.iter().filter_map(|v| v.upgrade()).filter(|u| create_session_param.additional_participants.iter().any(|p| *p == u.pid)); + let players = old_gathering + .connected_players + .iter() + .filter_map(|v| v.upgrade()) + .filter(|u| { + create_session_param + .additional_participants + .iter() + .any(|p| *p == u.pid) + }); for player in players { joining_players.push(Arc::downgrade(&player)); } } - drop(users); new_session.session.participation_count = create_session_param.participation_count as u32; @@ -266,20 +217,43 @@ impl MatchmakeExtension for User { &self, join_session_param: JoinMatchmakeSessionParam, ) -> Result { - let session = self.matchmake_manager.get_session(join_session_param.gid).await?; + let session = self + .matchmake_manager + .get_session(join_session_param.gid) + .await?; let mut session = session.lock().await; + if session.session.user_password_enabled { + if join_session_param.user_password != session.session.user_password { + return Err(ErrorCode::RendezVous_InvalidPassword); + } + } - session.connected_players.retain(|v| v.upgrade().is_some_and(|v| v.pid != self.pid)); + session + .connected_players + .retain(|v| v.upgrade().is_some_and(|v| v.pid != self.pid)); let mut joining_players = vec![self.this.clone()]; let users = self.matchmake_manager.users.read().await; - if let Ok(old_gathering) = self.matchmake_manager.get_session(join_session_param.gid_for_participation_check).await { + if let Ok(old_gathering) = self + .matchmake_manager + .get_session(join_session_param.gid_for_participation_check) + .await + { let old_gathering = old_gathering.lock().await; - let players = old_gathering.connected_players.iter().filter_map(|v| v.upgrade()).filter(|u| join_session_param.additional_participants.iter().any(|p| *p == u.pid)); + let players = old_gathering + .connected_players + .iter() + .filter_map(|v| v.upgrade()) + .filter(|u| { + join_session_param + .additional_participants + .iter() + .any(|p| *p == u.pid) + }); for player in players { joining_players.push(Arc::downgrade(&player)); } @@ -296,17 +270,28 @@ impl MatchmakeExtension for User { Ok(mm_session) } - async fn auto_matchmake_with_param_postpone(&self, param: AutoMatchmakeParam) -> Result { + 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 { + if let Ok(old_gathering) = self + .matchmake_manager + .get_session(param.gid_for_participation_check) + .await + { let old_gathering = old_gathering.lock().await; - let players = old_gathering.connected_players.iter().filter_map(|v| v.upgrade()).filter(|u| param.additional_participants.iter().any(|p| *p == u.pid)); + let players = old_gathering + .connected_players + .iter() + .filter_map(|v| v.upgrade()) + .filter(|u| param.additional_participants.iter().any(|p| *p == u.pid)); for player in players { joining_players.push(Arc::downgrade(&player)); } @@ -333,7 +318,9 @@ impl MatchmakeExtension for User { } if bool_matched_criteria { - session.add_players(&joining_players, param.join_message).await; + session + .add_players(&joining_players, param.join_message) + .await; return Ok(session.session.clone()); } @@ -359,17 +346,26 @@ impl MatchmakeExtension for User { create_matchmake_session_option: 0, matchmake_session, additional_participants, - }).await + }) + .await } - async fn find_matchmake_session_by_gathering_id_detail(&self, gid: u32) -> Result { + 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> { + async fn modify_current_game_attribute( + &self, + gid: u32, + attrib_index: u32, + attrib_val: u32, + ) -> Result<(), ErrorCode> { let session = self.matchmake_manager.get_session(gid).await?; let mut session = session.lock().await; @@ -388,30 +384,30 @@ impl Matchmake for User { let session = session.lock().await; - let urls: Vec<_> = - session - .connected_players - .iter() - .filter_map(|v| v.upgrade()) - .filter(|u| u.pid == session.session.gathering.host_pid) - .map(|u| async move { - u.station_url.read().await.clone() - }) - .next() - .ok_or(ErrorCode::RendezVous_SessionClosed)? - .await; - + let urls: Vec<_> = session + .connected_players + .iter() + .filter_map(|v| v.upgrade()) + .filter(|u| u.pid == session.session.gathering.host_pid) + .map(|u| async move { u.station_url.read().await.clone() }) + .next() + .ok_or(ErrorCode::RendezVous_SessionClosed)? + .await; println!("{:?}", urls); - - if urls.is_empty(){ - return Err(ErrorCode::RendezVous_NotParticipatedGathering) + + if urls.is_empty() { + return Err(ErrorCode::RendezVous_NotParticipatedGathering); } Ok(urls) } - async fn update_session_host(&self, gid: u32, change_session_owner: bool) -> Result<(), ErrorCode> { + async fn update_session_host( + &self, + gid: u32, + change_session_owner: bool, + ) -> Result<(), ErrorCode> { let session = self.matchmake_manager.get_session(gid).await?; let mut session = session.lock().await; @@ -422,40 +418,50 @@ impl Matchmake for User { continue; }; - player.remote.process_notification_event(NotificationEvent { - notif_type: 110000, - pid_source: self.pid, - param_1: gid, - param_2: self.pid, - param_3: 0, - str_param: "".to_string(), - }).await; + player + .remote + .process_notification_event(NotificationEvent { + notif_type: 110000, + pid_source: self.pid, + param_1: gid, + param_2: self.pid, + param_3: 0, + str_param: "".to_string(), + }) + .await; } if change_session_owner { session.session.gathering.owner_pid = self.pid; - for player in &session.connected_players { let Some(player) = player.upgrade() else { continue; }; - player.remote.process_notification_event(NotificationEvent { - notif_type: 4000, - pid_source: self.pid, - param_1: gid, - param_2: self.pid, - param_3: 0, - str_param: "".to_string(), - }).await; + player + .remote + .process_notification_event(NotificationEvent { + notif_type: 4000, + pid_source: self.pid, + param_1: gid, + param_2: self.pid, + param_3: 0, + str_param: "".to_string(), + }) + .await; } } Ok(()) } - async fn migrate_gathering_ownership(&self, gid: u32, candidates: Vec, _participants_only: bool) -> Result<(), ErrorCode> { + 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; @@ -468,14 +474,17 @@ impl Matchmake for User { continue; }; - player.remote.process_notification_event(NotificationEvent { - notif_type: 4000, - pid_source: self.pid, - param_1: gid, - param_2: *candidate, - param_3: 0, - str_param: "".to_string(), - }).await; + player + .remote + .process_notification_event(NotificationEvent { + notif_type: 4000, + pid_source: self.pid, + param_1: gid, + param_2: *candidate, + param_3: 0, + str_param: "".to_string(), + }) + .await; } Ok(()) @@ -487,12 +496,12 @@ impl MatchmakeExt for User { let session = self.matchmake_manager.get_session(gid).await?; let mut session = session.lock().await; - session.remove_player_from_session(self.pid, &message).await?; + session + .remove_player_from_session(self.pid, &message) + .await?; Ok(true) } - - } impl NatTraversal for User { @@ -507,7 +516,7 @@ impl NatTraversal for User { for station_url in urls.iter_mut() { station_url.options.retain(|o| match o { NatMapping(_) | NatFiltering(_) => false, - _ => true + _ => true, }); station_url.options.push(NatMapping(nat_mapping as u8)); @@ -517,7 +526,12 @@ impl NatTraversal for User { Ok(()) } - async fn report_nat_traversal_result(&self, _cid: u32, _result: bool, _rtt: u32) -> Result<(), ErrorCode> { + async fn report_nat_traversal_result( + &self, + _cid: u32, + _result: bool, + _rtt: u32, + ) -> Result<(), ErrorCode> { Ok(()) } @@ -526,17 +540,28 @@ impl NatTraversal for User { Err(RendezVous_AccountExpired) } - async fn request_probe_initialization_ext(&self, target_list: Vec, station_to_probe: String) -> Result<(), ErrorCode> { + 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); + println!( + "requesting station probe for {:?} to {:?}", + target_list, station_to_probe + ); for target in target_list { let Ok(url) = StationUrl::try_from(target.as_ref()) else { continue; }; - let Some(RVConnectionID(v)) = url.options.into_iter().find(|o| { matches!(o, &RVConnectionID(_)) }) else { + let Some(RVConnectionID(v)) = url + .options + .into_iter() + .find(|o| matches!(o, &RVConnectionID(_))) + else { continue; }; @@ -548,7 +573,9 @@ impl NatTraversal for User { continue; }; - user.remote.request_probe_initiation(station_to_probe.clone()).await; + user.remote + .request_probe_initiation(station_to_probe.clone()) + .await; } info!("finished probing"); @@ -557,6 +584,4 @@ impl NatTraversal for User { } } -impl Ranking for User{ - -} \ No newline at end of file +impl Ranking for User {} 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/src/prudp/sockaddr.rs b/rnex-core/src/prudp/socket_addr.rs similarity index 54% rename from src/prudp/sockaddr.rs rename to rnex-core/src/prudp/socket_addr.rs index 4e42fcc..4ffac83 100644 --- a/src/prudp/sockaddr.rs +++ b/rnex-core/src/prudp/socket_addr.rs @@ -1,37 +1,38 @@ +use hmac::Hmac; +use macros::RmcSerialize; +use md5::digest::Mac; +use rnex_core::prudp::virtual_port::VirtualPort; use std::io::Write; use std::net::SocketAddrV4; -use hmac::{Hmac, Mac}; -use macros::RmcSerialize; -use crate::prudp::packet::VirtualPort; type Md5Hmac = Hmac; #[derive(Eq, PartialEq, Hash, Debug, Copy, Clone, Ord, PartialOrd, RmcSerialize)] #[rmc_struct(0)] -pub struct PRUDPSockAddr{ +pub struct PRUDPSockAddr { pub regular_socket_addr: SocketAddrV4, - pub virtual_port: VirtualPort + pub virtual_port: VirtualPort, } - - -impl PRUDPSockAddr{ - - pub fn new(regular_socket_addr: SocketAddrV4, virtual_port: VirtualPort) -> Self{ - Self{ +impl PRUDPSockAddr { + pub fn new(regular_socket_addr: SocketAddrV4, virtual_port: VirtualPort) -> Self { + Self { regular_socket_addr, - virtual_port + virtual_port, } } - pub(super) fn calculate_connection_signature(&self) -> [u8; 16] { + pub fn calculate_connection_signature(&self) -> [u8; 16] { let mut hmac = Md5Hmac::new_from_slice(&[0; 16]).expect("fuck"); - let mut data = self.regular_socket_addr.ip().octets().to_vec(); + let data = self.regular_socket_addr.ip().octets().to_vec(); //data.extend_from_slice(&self.regular_socket_addr.port().to_be_bytes()); - hmac.write_all(&data).expect("figuring this out was complete ass"); - let result: [u8; 16] = hmac.finalize().into_bytes()[0..16].try_into().expect("fuck"); + hmac.write_all(&data) + .expect("figuring this out was complete ass"); + let result: [u8; 16] = hmac.finalize().into_bytes()[0..16] + .try_into() + .expect("fuck"); result } -} \ No newline at end of file +} diff --git a/src/prudp/station_url.rs b/rnex-core/src/prudp/station_url.rs similarity index 75% rename from src/prudp/station_url.rs rename to rnex-core/src/prudp/station_url.rs index 441c1b3..a4665c2 100644 --- a/src/prudp/station_url.rs +++ b/rnex-core/src/prudp/station_url.rs @@ -5,6 +5,7 @@ use std::io::Read; use crate::prudp::station_url::Type::{PRUDP, PRUDPS, UDP}; use crate::prudp::station_url::UrlOptions::{Address, ConnectionID, NatFiltering, NatMapping, NatType, Platform, PMP, Port, PrincipalID, RVConnectionID, StreamID, StreamType, UPNP, PID}; use crate::rmc::structures::Error::StationUrlInvalid; +use crate::rmc::structures::helpers::DummyFormatWriter; use crate::rmc::structures::RmcSerialize; #[derive(Clone, Copy, PartialEq, Eq)] pub enum Type{ @@ -111,7 +112,7 @@ impl StationUrl{ impl TryFrom<&str> for StationUrl{ type Error = (); - + fn try_from(value: &str) -> Result { let (url_type, options) = value.split_at(value.find(":/").ok_or(())?); @@ -135,59 +136,65 @@ impl TryFrom<&str> for StationUrl{ } } -impl<'a> Into for &'a StationUrl{ - fn into(self) -> String { - let mut url = match self.url_type{ +impl Display for StationUrl{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let url_type_str = match self.url_type{ UDP => "udp:/", PRUDP => "prudp:/", PRUDPS => "prudps:/" - }.to_owned(); + }; + write!(f, "{}",url_type_str)?; for option in &self.options{ match option{ - Address(v) => write!(url, "address={}", v).expect("failed to write"), - Port(v) => write!(url, "port={}", v).expect("failed to write"), - StreamType(v) => write!(url, "stream={}", v).expect("failed to write"), - StreamID(v) => write!(url, "sid={}", v).expect("failed to write"), - ConnectionID(v) => write!(url, "CID={}", v).expect("failed to write"), - PrincipalID(v) => write!(url, "PID={}", v).expect("failed to write"), - NatType(v) => write!(url, "type={}", v).expect("failed to write"), - NatMapping(v) => write!(url, "natm={}", v).expect("failed to write"), - NatFiltering(v) => write!(url, "natf={}", v).expect("failed to write"), - UPNP(v) => write!(url, "upnp={}", v).expect("failed to write"), - RVConnectionID(v) => write!(url, "RVCID={}", v).expect("failed to write"), - Platform(v) => write!(url, "pl={}", v).expect("failed to write"), - PMP(v) => write!(url, "pmp={}", v).expect("failed to write"), - PID(v) => write!(url, "PID={}", v).expect("failed to write"), + Address(v) => write!(f, "address={}", v)?, + Port(v) => write!(f, "port={}", v)?, + StreamType(v) => write!(f, "stream={}", v)?, + StreamID(v) => write!(f, "sid={}", v)?, + ConnectionID(v) => write!(f, "CID={}", v)?, + PrincipalID(v) => write!(f, "PID={}", v)?, + NatType(v) => write!(f, "type={}", v)?, + NatMapping(v) => write!(f, "natm={}", v)?, + NatFiltering(v) => write!(f, "natf={}", v)?, + UPNP(v) => write!(f, "upnp={}", v)?, + RVConnectionID(v) => write!(f, "RVCID={}", v)?, + Platform(v) => write!(f, "pl={}", v)?, + PMP(v) => write!(f, "pmp={}", v)?, + PID(v) => write!(f, "PID={}", v)?, } - write!(url, ";").expect("failed to write"); + write!(f, ";")?; } + Ok(()) + } +} +impl<'a> Into for &'a StationUrl{ + fn into(self) -> String { + let url = self.to_string(); + url[0..url.len()-1].into() } } -impl Display for StationUrl{ - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let str: String = self.into(); - - write!(f, "{}", str) - } - - -} - impl RmcSerialize for StationUrl{ - fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { + 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 dyn std::io::Write) -> crate::rmc::structures::Result<()> { + fn serialize(&self, writer: &mut impl std::io::Write) -> crate::rmc::structures::Result<()> { let str: String = self.into(); str.serialize(writer) } + + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + let mut dummy = DummyFormatWriter::new(); + + write!(&mut dummy, "{}", self)?; + + Ok(dummy.serialize_str_len()) + } } impl Debug for StationUrl{ diff --git a/rnex-core/src/prudp/ticket.rs b/rnex-core/src/prudp/ticket.rs new file mode 100644 index 0000000..9fd094d --- /dev/null +++ b/rnex-core/src/prudp/ticket.rs @@ -0,0 +1,81 @@ +use std::io::Cursor; + +use log::{error, info}; +use rc4::{KeyInit, Rc4, Rc4Core, StreamCipher, cipher::StreamCipherCoreWrapper}; +use typenum::{U16, U32}; +use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; + +use crate::{ + kerberos::{SESSION_KEY_LENGTH, SESSION_KEY_LENGTH_TY, TicketInternalData, derive_key}, + nex::account::Account, + rmc::structures::RmcSerialize, +}; + +pub fn read_secure_connection_data( + data: &[u8], + act: &Account, +) -> Option<([u8; SESSION_KEY_LENGTH], u32, u32)> { + let mut cursor = Cursor::new(data); + + let mut ticket_data: Vec = Vec::deserialize(&mut cursor).ok()?; + let mut request_data: Vec = Vec::deserialize(&mut cursor).ok()?; + info!( + "done + request data" + ); + + let ticket_data_size = ticket_data.len(); + + let ticket_data = &mut ticket_data[0..ticket_data_size - 0x10]; + + let server_key = derive_key(act.pid, &act.kerbros_password[..]); + + let mut rc4: StreamCipherCoreWrapper> = + 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)) +} 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 97% rename from src/rmc/message.rs rename to rnex-core/src/rmc/message.rs index f45b585..cf951bf 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)] 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/src/rmc/protocols/auth.rs b/rnex-core/src/rmc/protocols/auth.rs similarity index 83% rename from src/rmc/protocols/auth.rs rename to rnex-core/src/rmc/protocols/auth.rs index ed8d1cd..594d9dd 100644 --- a/src/rmc/protocols/auth.rs +++ b/rnex-core/src/rmc/protocols/auth.rs @@ -1,18 +1,20 @@ -use crate::rmc::response::ErrorCode; -use crate::rmc::structures::any::Any; -use crate::rmc::structures::connection_data::ConnectionData; -use crate::rmc::structures::qresult::QResult; +use crate::rmc::structures::connection_data::{ConnectionData, ConnectionDataOld}; use macros::{method_id, rmc_proto}; +use rnex_core::rmc::response::ErrorCode; +use rnex_core::rmc::structures::any::Any; +use rnex_core::rmc::structures::qresult::QResult; - -/// This is the representation for `Ticket Granting`(for details see the +/// This is the representation for `Ticket Granting`(for details see the /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) #[rmc_proto(10)] pub trait Auth { /// representation of the `Login` method(for details see the /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) #[method_id(1)] - async fn login(&self, name: String) -> Result<(), ErrorCode>; + async fn login( + &self, + name: String, + ) -> Result<(QResult, u32, Vec, ConnectionDataOld, String), ErrorCode>; /// representation of the `LoginEx` method(for details see the /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) diff --git a/rnex-core/src/rmc/protocols/friends.rs b/rnex-core/src/rmc/protocols/friends.rs new file mode 100644 index 0000000..775016e --- /dev/null +++ b/rnex-core/src/rmc/protocols/friends.rs @@ -0,0 +1,146 @@ +use macros::{RmcSerialize, method_id, rmc_proto}; + +use rnex_core::{kerberos::KerberosDateTime, rmc::response::ErrorCode}; + +#[derive(RmcSerialize)] +#[rmc_struct(0)] +pub struct MiiV2 { + pub name: String, + pub unk: u8, + pub unk2: u8, + pub mii_data: Vec, + pub date_time: KerberosDateTime, +} + +#[derive(RmcSerialize)] +#[rmc_struct(0)] +pub struct PrincipalBasicInfo { + pub pid: u32, + pub nnid: String, + pub mii: MiiV2, + pub unk: u8, +} + +#[derive(RmcSerialize)] +#[rmc_struct(0)] +pub struct NNAInfo { + pub principal_basic_info: PrincipalBasicInfo, + pub unk: u8, + pub unk2: u8, +} + +#[derive(RmcSerialize)] +#[rmc_struct(0)] +pub struct GameKey { + pub tid: u64, + pub version: u16, +} + +#[derive(RmcSerialize)] +#[rmc_struct(0)] +pub struct NintendoPresenceV2 { + pub changed_flags: u32, + pub is_online: bool, + pub game_key: GameKey, + pub unk: u8, + pub message: String, + pub unk2: u32, + pub unk3: u8, + pub game_server_id: u32, + pub unk4: u32, + pub pid: u32, + pub gid: u32, + pub app_data: Vec, + pub unk5: u8, + pub unk6: u8, + pub unk7: u8, +} +#[derive(RmcSerialize)] +#[rmc_struct(0)] +pub struct PrincipalPreference { + pub show_online: bool, + pub show_playing_title: bool, + pub block_friend_request: bool, +} + +#[derive(RmcSerialize)] +#[rmc_struct(0)] +pub struct Comment { + pub unk: u8, + pub message: String, + pub last_changed: KerberosDateTime, +} + +#[derive(RmcSerialize)] +#[rmc_struct(0)] +pub struct FriendInfo { + pub nna_info: NNAInfo, + pub presence: NintendoPresenceV2, + pub comment: Comment, + pub became_friends: KerberosDateTime, + pub last_online: KerberosDateTime, + pub unk: u64, +} + +#[derive(RmcSerialize)] +#[rmc_struct(0)] +pub struct FriendRequestMessage { + pub friend_request_id: u64, + pub is_recieved: u8, + pub unk: u8, + pub message: String, + pub unk2: u8, + pub unk3: String, + pub game_key: GameKey, + pub unk4: KerberosDateTime, + pub expires_on: KerberosDateTime, +} + +#[derive(RmcSerialize)] +#[rmc_struct(0)] +pub struct FriendRequest { + pub basic_info: PrincipalBasicInfo, + pub request_message: FriendRequestMessage, + pub sent_on: KerberosDateTime, +} + +#[derive(RmcSerialize)] +#[rmc_struct(0)] +pub struct BlacklistedPrincipal { + pub basic_info: PrincipalBasicInfo, + pub game_key: GameKey, + pub since: KerberosDateTime, +} +#[derive(RmcSerialize)] +#[rmc_struct(0)] +pub struct PersistentNotification { + pub unk1: u64, + pub unk2: u32, + pub unk3: u32, + pub unk4: u32, + pub unk5: String, +} + +#[rmc_proto(102)] +pub trait Friends { + #[method_id(1)] + async fn update_and_get_all_information( + &self, + info: NNAInfo, + presence: NintendoPresenceV2, + date_time: KerberosDateTime, + ) -> Result< + ( + PrincipalPreference, + Comment, + Vec, + Vec, + Vec, + Vec, + bool, + Vec, + bool, + ), + ErrorCode, + >; +} diff --git a/src/rmc/protocols/matchmake.rs b/rnex-core/src/rmc/protocols/matchmake.rs similarity index 86% rename from src/rmc/protocols/matchmake.rs rename to rnex-core/src/rmc/protocols/matchmake.rs index 1c2d5e5..595127a 100644 --- a/src/rmc/protocols/matchmake.rs +++ b/rnex-core/src/rmc/protocols/matchmake.rs @@ -1,6 +1,6 @@ use macros::{method_id, rmc_proto}; -use crate::prudp::station_url::StationUrl; -use crate::rmc::response::ErrorCode; +use rnex_core::prudp::station_url::StationUrl; +use rnex_core::rmc::response::ErrorCode; #[rmc_proto(21)] pub trait Matchmake{ @@ -8,10 +8,8 @@ pub trait Matchmake{ async fn unregister_gathering(&self, gid: u32) -> Result; #[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 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..6896cf3 100644 --- a/src/rmc/protocols/mod.rs +++ b/rnex-core/src/rmc/protocols/mod.rs @@ -1,20 +1,22 @@ #![allow(async_fn_in_trait)] pub mod auth; -pub mod secure; -pub mod notifications; +pub mod friends; pub mod matchmake; +pub mod matchmake_ext; pub mod matchmake_extension; pub mod nat_traversal; -pub mod matchmake_ext; +pub mod notifications; pub mod ranking; +pub mod secure; -use crate::util::{SendingBufferConnection, SplittableBufferConnection}; +use crate::result::ResultExtension; use crate::rmc::message::RMCMessage; use crate::rmc::protocols::RemoteCallError::ConnectionBroke; use crate::rmc::response::{ErrorCode, RMCResponse, RMCResponseResult}; use crate::rmc::structures; use crate::rmc::structures::RmcSerialize; +use crate::util::{SendingBufferConnection, SplittableBufferConnection}; use log::{error, info}; use std::collections::HashMap; use std::future::Future; @@ -24,8 +26,7 @@ use std::sync::Arc; use std::time::Duration; use thiserror::Error; use tokio::sync::{Mutex, Notify}; -use tokio::time::{sleep, sleep_until, Instant}; -use crate::result::ResultExtension; +use tokio::time::{Instant, sleep, sleep_until}; #[derive(Error, Debug)] pub enum RemoteCallError { @@ -68,7 +69,7 @@ impl RmcConnection { Ok(()) } - pub async fn disconnect(&self){ + pub async fn disconnect(&self) { self.0.disconnect().await; } } @@ -92,15 +93,11 @@ impl RmcResponseReceiver { let mut locked = self.1.lock().await; if let Some(v) = locked.remove(&call_id) { - match v.response_result{ - RMCResponseResult::Success { - data, - .. - } => return Ok(data), - RMCResponseResult::Error { - error_code, - .. - } => return Err(RemoteCallError::ServerError(error_code)) + match v.response_result { + RMCResponseResult::Success { data, .. } => return Ok(data), + RMCResponseResult::Error { error_code, .. } => { + return Err(RemoteCallError::ServerError(error_code)); + } } } @@ -153,8 +150,9 @@ macro_rules! define_rmc_proto { $($protocol:path),* }) => { paste::paste!{ + #[allow(unused_variables)] pub trait []: 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 +162,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 +204,23 @@ impl RmcCallable for () { } } -pub trait RemoteInstantiatable{ +pub trait RmcPureRemoteObject { fn new(conn: RmcConnection) -> Self; +} + +pub trait RemoteDisconnectable { async fn disconnect(&self); } -pub struct OnlyRemote(T); +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 +228,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 +256,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 +284,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 +331,12 @@ where { let exposed_object = exposed_object.clone(); tokio::spawn(async move { - handle_incoming( - conn, - exposed_object, - notify, - incoming - ).await; + handle_incoming(conn, exposed_object, notify, incoming).await; }); - tokio::spawn(async move { - while sending_conn.is_alive(){ - sending_conn.send([0,0,0,0,0].to_vec()).await; + while sending_conn.is_alive() { + sending_conn.send([0, 0, 0, 0, 0].to_vec()).await; sleep(Duration::from_secs(10)).await; } }); @@ -331,12 +345,20 @@ where exposed_object } -impl 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/src/rmc/protocols/notifications.rs b/rnex-core/src/rmc/protocols/notifications.rs similarity index 100% rename from src/rmc/protocols/notifications.rs rename to rnex-core/src/rmc/protocols/notifications.rs diff --git a/rnex-core/src/rmc/protocols/ranking.rs b/rnex-core/src/rmc/protocols/ranking.rs new file mode 100644 index 0000000..93dcb87 --- /dev/null +++ b/rnex-core/src/rmc/protocols/ranking.rs @@ -0,0 +1,33 @@ +use macros::{rmc_struct, rmc_proto, RmcSerialize}; + +#[derive(RmcSerialize, Debug, Default, Clone)] +struct ResultsRange{ + offset: u32, + size: u32 +} + +#[derive(RmcSerialize, Debug, Default, Clone)] +#[rmc_struct(1)] +struct CompetitionRankingGetParam{ + unk: u32, + range: ResultsRange, + festival_ids: Vec, +} + +#[derive(RmcSerialize, Debug, Default, Clone)] +#[rmc_struct(0)] +struct CompetitionRankingScoreInfo{ + fest_id: u32, + score_data: Vec, + unk: u32, + team_wins: Vec, + team_votes: Vec +} + + + +#[rmc_proto(112)] +pub trait Ranking{ + //#[method_id(16)] + //async fn competition_ranking_get_param(&self, param: CompetitionRankingGetParam) -> Result,ErrorCode>; +} \ No newline at end of file diff --git a/rnex-core/src/rmc/protocols/secure.rs b/rnex-core/src/rmc/protocols/secure.rs new file mode 100644 index 0000000..3df23c6 --- /dev/null +++ b/rnex-core/src/rmc/protocols/secure.rs @@ -0,0 +1,23 @@ +use macros::{method_id, rmc_proto}; +use rnex_core::prudp::station_url::StationUrl; +use rnex_core::rmc::response::ErrorCode; +use rnex_core::rmc::structures::qresult::QResult; + +use crate::rmc::structures::any::Any; + +#[rmc_proto(11)] +pub trait Secure { + #[method_id(1)] + async fn register( + &self, + station_urls: Vec, + ) -> 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 99% rename from src/rmc/response.rs rename to rnex-core/src/rmc/response.rs index dcdebc6..c994f8b 100644 --- a/src/rmc/response.rs +++ b/rnex-core/src/rmc/response.rs @@ -7,8 +7,8 @@ use std::io::{Read, Seek, Write}; use std::mem::transmute; use bytemuck::bytes_of; use log::error; -use v_byte_macros::EnumTryInto; -use crate::endianness::{ReadExtensions, IS_BIG_ENDIAN}; +use v_byte_helpers::EnumTryInto; +use v_byte_helpers::{ReadExtensions, IS_BIG_ENDIAN}; use crate::rmc::response::ErrorCode::Core_Exception; use crate::rmc::structures::qresult::ERROR_MASK; use crate::util::SendingBufferConnection; @@ -471,7 +471,7 @@ mod test { #[test] fn test() { - let mut data_orig = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 69, 4, 20]; + let data_orig = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 69, 4, 20]; let mut data = data_orig; let mut rc4: Rc4 = diff --git a/src/rmc/structures/any.rs b/rnex-core/src/rmc/structures/any.rs similarity index 60% rename from src/rmc/structures/any.rs rename to rnex-core/src/rmc/structures/any.rs index d402f3e..c4dc2ce 100644 --- a/src/rmc/structures/any.rs +++ b/rnex-core/src/rmc/structures/any.rs @@ -1,15 +1,15 @@ +use rnex_core::rmc::structures::{Result, RmcSerialize}; use std::io::{Read, Write}; -use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; -use super::{Result, RmcSerialize}; +use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; #[derive(Debug, Default)] -pub struct Any{ +pub struct Any { pub name: String, - pub data: Vec + pub data: Vec, } -impl RmcSerialize for Any{ - fn serialize(&self, writer: &mut dyn Write) -> Result<()> { +impl RmcSerialize for Any { + fn serialize(&self, writer: &mut impl Write) -> Result<()> { self.name.serialize(writer)?; let u32_len = self.data.len() as u32; @@ -21,7 +21,7 @@ impl RmcSerialize for Any{ Ok(()) } - fn deserialize(mut reader: &mut dyn Read) -> Result { + fn deserialize(reader: &mut impl Read) -> Result { let name = String::deserialize(reader)?; // also length ? @@ -32,11 +32,6 @@ impl RmcSerialize for Any{ reader.read_exact(&mut data)?; - Ok( - Any{ - name, - data - } - ) + Ok(Any { name, data }) } -} \ No newline at end of file +} 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/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/src/rmc/structures/list.rs b/rnex-core/src/rmc/structures/list.rs similarity index 53% rename from src/rmc/structures/list.rs rename to rnex-core/src/rmc/structures/list.rs index 870be65..890be9a 100644 --- a/src/rmc/structures/list.rs +++ b/rnex-core/src/rmc/structures/list.rs @@ -1,9 +1,7 @@ -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 v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; use crate::rmc::structures::RmcSerialize; @@ -11,7 +9,7 @@ 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<()> { + 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))?; @@ -22,21 +20,27 @@ impl RmcSerialize for Vec{ Ok(()) } - fn deserialize(mut reader: &mut dyn Read) -> crate::rmc::structures::Result { + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { let len: u32 = reader.read_struct(IS_BIG_ENDIAN)?; - let mut vec = Vec::with_capacity(len as usize); + //let mut vec = Vec::with_capacity(len as usize); - for _ in 0..len{ - vec.push(T::deserialize(reader)?); - } + let 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 dyn Write) -> crate::rmc::structures::Result<()> { + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { for i in 0..LEN{ self[i].serialize(writer)?; } @@ -44,7 +48,7 @@ impl RmcSerialize for [T; LEN]{ Ok(()) } - fn deserialize(reader: &mut dyn Read) -> crate::rmc::structures::Result { + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { let mut arr = [const { MaybeUninit::::uninit() }; LEN]; for i in 0..LEN{ @@ -57,4 +61,12 @@ impl RmcSerialize for [T; LEN]{ 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 97% rename from src/rmc/structures/matchmake.rs rename to rnex-core/src/rmc/structures/matchmake.rs index 28e851b..9194b97 100644 --- a/src/rmc/structures/matchmake.rs +++ b/rnex-core/src/rmc/structures/matchmake.rs @@ -1,5 +1,5 @@ -use crate::kerberos::KerberosDateTime; -use crate::rmc::structures::variant::Variant; +use rnex_core::kerberos::KerberosDateTime; +use rnex_core::rmc::structures::variant::Variant; use macros::RmcSerialize; // rmc structure diff --git a/rnex-core/src/rmc/structures/mod.rs b/rnex-core/src/rmc/structures/mod.rs new file mode 100644 index 0000000..7a1610c --- /dev/null +++ b/rnex-core/src/rmc/structures/mod.rs @@ -0,0 +1,77 @@ +use std::{fmt, io}; +use std::io::{Read, Write}; +use std::string::FromUtf8Error; +use thiserror::Error; +use crate::rmc::structures::helpers::DummyWriter; +//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 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; +pub mod networking; +pub mod helpers; + +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 mut data = Vec::with_capacity( + self.serialize_write_size()? as usize + ); + + self.serialize(&mut data)?; + + debug_assert_eq!(self.serialize_write_size().unwrap(), data.len() as u32); + + Ok(data) + } +} + +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) + } + + +} \ No newline at end of file diff --git a/rnex-core/src/rmc/structures/networking.rs b/rnex-core/src/rmc/structures/networking.rs new file mode 100644 index 0000000..03627af --- /dev/null +++ b/rnex-core/src/rmc/structures/networking.rs @@ -0,0 +1,39 @@ +use std::io::{Read, Write}; +use std::net::{Ipv4Addr, SocketAddrV4}; +use rnex_core::prudp::virtual_port::VirtualPort; +use crate::rmc::structures::RmcSerialize; + +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 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) + } +} \ No newline at end of file diff --git a/rnex-core/src/rmc/structures/primitives.rs b/rnex-core/src/rmc/structures/primitives.rs new file mode 100644 index 0000000..3bdd835 --- /dev/null +++ b/rnex-core/src/rmc/structures/primitives.rs @@ -0,0 +1,479 @@ +use crate::rmc::structures::RmcSerialize; +use bytemuck::bytes_of; +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 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.2.serialize_write_size()? + + self.3.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 73% rename from src/rmc/structures/qbuffer.rs rename to rnex-core/src/rmc/structures/qbuffer.rs index bb6c235..fb30e64 100644 --- a/src/rmc/structures/qbuffer.rs +++ b/rnex-core/src/rmc/structures/qbuffer.rs @@ -1,6 +1,6 @@ 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}; @@ -8,7 +8,7 @@ use crate::rmc::structures::{Result, RmcSerialize}; 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 +17,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/src/rmc/structures/qresult.rs b/rnex-core/src/rmc/structures/qresult.rs similarity index 77% rename from src/rmc/structures/qresult.rs rename to rnex-core/src/rmc/structures/qresult.rs index 41a5263..312540a 100644 --- a/src/rmc/structures/qresult.rs +++ b/rnex-core/src/rmc/structures/qresult.rs @@ -1,7 +1,7 @@ use std::io::{Read, Write}; use bytemuck::{bytes_of, Pod, Zeroable}; -use v_byte_macros::SwapEndian; -use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; +use v_byte_helpers::SwapEndian; +use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; use crate::rmc::response::ErrorCode; use crate::rmc::structures::{RmcSerialize, Result}; @@ -26,12 +26,12 @@ impl QResult{ } impl RmcSerialize for QResult{ - fn serialize(&self, writer: &mut dyn Write) -> Result<()> { + fn serialize(&self, writer: &mut impl Write) -> Result<()> { writer.write(bytes_of(self))?; Ok(()) } - fn deserialize(mut reader: &mut dyn Read) -> Result { + fn deserialize(reader: &mut impl Read) -> Result { Ok(reader.read_struct(IS_BIG_ENDIAN)?) } } \ No newline at end of file diff --git a/rnex-core/src/rmc/structures/ranking.rs b/rnex-core/src/rmc/structures/ranking.rs new file mode 100644 index 0000000..1782cba --- /dev/null +++ b/rnex-core/src/rmc/structures/ranking.rs @@ -0,0 +1,74 @@ +use bytemuck::{Pod, Zeroable}; +use macros::RmcSerialize; +use rnex_core::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 crate::rmc::structures::ranking::{UploadCompetitionData, UserData}; + use bytemuck::from_bytes; + use rnex_core::rmc::structures::RmcSerialize; + use std::io::Cursor; + + #[test] + fn test() { + return; + 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()) + } +} 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..52184bb --- /dev/null +++ b/rnex-core/src/rmc/structures/rmc_struct.rs @@ -0,0 +1,113 @@ + +use std::cmp::max; +use std::fmt::Arguments; +use std::io; +use std::io::{ErrorKind, IoSlice, Read, Write}; +use crate::rmc::structures::Result; + + +#[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::ReadExtensions; + use v_byte_helpers::IS_BIG_ENDIAN; + 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..1231956 --- /dev/null +++ b/rnex-core/src/rmc/structures/string.rs @@ -0,0 +1,48 @@ +use std::io::{Read, Write}; +use bytemuck::bytes_of; +use log::error; +use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; +use super::{Result, RmcSerialize}; + +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/src/rnex_proxy_common.rs b/rnex-core/src/rnex_proxy_common.rs similarity index 78% rename from src/rnex_proxy_common.rs rename to rnex-core/src/rnex_proxy_common.rs index c300aa0..29cb161 100644 --- a/src/rnex_proxy_common.rs +++ b/rnex-core/src/rnex_proxy_common.rs @@ -1,5 +1,5 @@ use macros::RmcSerialize; -use crate::prudp::sockaddr::PRUDPSockAddr; +use crate::prudp::socket_addr::PRUDPSockAddr; #[derive(Debug, RmcSerialize)] #[rmc_struct(0)] 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/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/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/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/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/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/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/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