diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index a1f51b3..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Build - -on: - push: - branches: - - main - pull_request: - branches: - - main - -env: - CARGO_TERM_COLOR: always - -jobs: - build: - runs-on: self-hosted - steps: - - name: Checkout Repository And Submodules - uses: actions/checkout@v1 - with: - submodules: recursive - - - name: Setup Rust - run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -q -y - - - name: Build - run: . "$HOME/.cargo/env" && cargo build --verbose - - - name: Run tests - run: . "$HOME/.cargo/env" && cargo test --verbose \ No newline at end of file diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml deleted file mode 100644 index 4618492..0000000 --- a/.github/workflows/stale.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: 'Close stale issues and PRs' -on: - schedule: - - cron: '30 1 * * *' - -jobs: - stale: - runs-on: debian-latest - steps: - - uses: actions/stale@v9 - with: - stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.' - days-before-stale: 10 - days-before-close: 5 diff --git a/Cargo.lock b/Cargo.lock index 0fbdcee..7fabb1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,7 +66,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -77,7 +77,22 @@ checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", +] + +[[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]] @@ -127,8 +142,8 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http", - "http-body", + "http 1.2.0", + "http-body 1.0.1", "http-body-util", "itoa", "matchit", @@ -153,8 +168,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 1.2.0", + "http-body 1.0.1", "http-body-util", "mime", "pin-project-lite", @@ -176,7 +191,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -185,6 +200,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[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" @@ -204,7 +225,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.96", + "syn 2.0.98", "which", ] @@ -246,7 +267,7 @@ checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -298,7 +319,7 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -331,6 +352,17 @@ dependencies = [ "cc", ] +[[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-sys" version = "0.8.7" @@ -356,6 +388,39 @@ 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.98", +] + [[package]] name = "digest" version = "0.10.7" @@ -385,6 +450,15 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -407,6 +481,20 @@ 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.4.2" @@ -425,6 +513,20 @@ 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-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.31" @@ -432,6 +534,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -440,6 +543,12 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + [[package]] name = "futures-sink" version = "0.3.31" @@ -458,10 +567,28 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ + "futures-channel", "futures-core", + "futures-io", + "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]] @@ -487,17 +614,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.0-rc.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a78f88e84d239c7f2619ae8b091603c26208e1cb322571f5a29d6806f56ee5e" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", - "js-sys", "libc", - "rustix", - "wasi 0.13.3+wasi-0.2.2", - "wasm-bindgen", - "windows-targets", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -512,6 +636,25 @@ 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.7.1", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.7" @@ -523,7 +666,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http", + "http 1.2.0", "indexmap 2.7.1", "slab", "tokio", @@ -549,6 +692,18 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "hex" version = "0.4.3" @@ -573,6 +728,17 @@ 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.2.0" @@ -584,6 +750,17 @@ 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" @@ -591,7 +768,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.2.0", ] [[package]] @@ -602,8 +779,8 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http", - "http-body", + "http 1.2.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -619,6 +796,30 @@ 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" @@ -628,9 +829,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2", - "http", - "http-body", + "h2 0.4.7", + "http 1.2.0", + "http-body 1.0.1", "httparse", "httpdate", "itoa", @@ -646,7 +847,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ - "hyper", + "hyper 1.6.0", "hyper-util", "pin-project-lite", "tokio", @@ -662,9 +863,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", - "hyper", + "http 1.2.0", + "http-body 1.0.1", + "hyper 1.6.0", "pin-project-lite", "socket2", "tokio", @@ -713,8 +914,15 @@ checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown 0.15.2", + "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.3" @@ -724,6 +932,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "is-terminal" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "itertools" version = "0.12.1" @@ -783,7 +1002,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -792,19 +1011,54 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +[[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", - "syn 1.0.109", + "rand 0.9.0", + "syn 2.0.98", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", ] [[package]] @@ -861,6 +1115,25 @@ dependencies = [ "windows-sys 0.52.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.2.0", + "httparse", + "memchr", + "mime", + "spin", + "tokio", + "tokio-util", + "version_check", +] + [[package]] name = "multimap" version = "0.10.0" @@ -877,6 +1150,16 @@ dependencies = [ "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-conv" version = "0.1.0" @@ -892,6 +1175,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + [[package]] name = "num_threads" version = "0.1.7" @@ -916,12 +1209,64 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + [[package]] name = "paste" 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.98", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -955,7 +1300,7 @@ checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -992,7 +1337,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1004,6 +1349,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", + "version_check", + "yansi", +] + [[package]] name = "prost" version = "0.13.4" @@ -1030,7 +1388,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.96", + "syn 2.0.98", "tempfile", ] @@ -1044,7 +1402,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1065,6 +1423,12 @@ 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" @@ -1078,12 +1442,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.0-beta.3" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fccbfebb3972a41a31c605a59207d9fba5489b9a87d9d87024cb6df73a32ec7" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ - "rand_chacha 0.9.0-beta.1", - "rand_core 0.9.0-beta.1", + "rand_chacha 0.9.0", + "rand_core 0.9.3", "zerocopy 0.8.14", ] @@ -1099,12 +1463,12 @@ dependencies = [ [[package]] name = "rand_chacha" -version = "0.9.0-beta.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16da77124f4ee9fabd55ce6540866e9101431863b4876de58b68797f331adf2" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.0-beta.1", + "rand_core 0.9.3", ] [[package]] @@ -1118,12 +1482,11 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.0-beta.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a98fa0b8309344136abe6244130311e76997e546f76fae8054422a7539b43df7" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.0-rc.0", - "zerocopy 0.8.14", + "getrandom 0.3.2", ] [[package]] @@ -1135,6 +1498,35 @@ dependencies = [ "cipher", ] +[[package]] +name = "redox_syscall" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +dependencies = [ + "bitflags", +] + +[[package]] +name = "ref-cast" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + [[package]] name = "regex" version = "1.11.1" @@ -1143,8 +1535,17 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "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]] @@ -1155,9 +1556,15 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "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" @@ -1179,6 +1586,88 @@ dependencies = [ "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.7.1", + "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.7.1", + "proc-macro2", + "quote", + "rocket_http", + "syn 2.0.98", + "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.7.1", + "log", + "memchr", + "pear", + "percent-encoding", + "pin-project-lite", + "ref-cast", + "serde", + "smallvec", + "stable-pattern", + "state", + "time", + "tokio", + "uncased", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -1243,6 +1732,24 @@ version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +[[package]] +name = "ryu" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" + +[[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 = "serde" version = "1.0.217" @@ -1260,7 +1767,37 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", +] + +[[package]] +name = "serde_json" +version = "1.0.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", ] [[package]] @@ -1269,6 +1806,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "simplelog" version = "0.12.2" @@ -1316,6 +1862,7 @@ name = "splatoon-server-rust" version = "0.1.0" dependencies = [ "anyhow", + "async-trait", "bytemuck", "chrono", "dotenv", @@ -1325,19 +1872,41 @@ dependencies = [ "macros", "md-5", "once_cell", + "paste", "prost", - "rand 0.9.0-beta.3", + "rand 0.9.0", "rc4", + "rocket", "rustls", + "serde", "simplelog", "thiserror", "tokio", "tokio-stream", "tonic", "tonic-build", + "typenum", "v_byte_macros", ] +[[package]] +name = "stable-pattern" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4564168c00635f88eaed410d5efa8131afa8d8699a612c80c455a0ba05c21045" +dependencies = [ + "memchr", +] + +[[package]] +name = "state" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" +dependencies = [ + "loom", +] + [[package]] name = "subtle" version = "2.6.1" @@ -1357,9 +1926,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.96" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -1412,7 +1981,17 @@ checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", +] + +[[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", ] [[package]] @@ -1459,6 +2038,7 @@ dependencies = [ "libc", "mio", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.52.0", @@ -1472,7 +2052,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1499,6 +2079,40 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +dependencies = [ + "indexmap 2.7.1", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tonic" version = "0.12.3" @@ -1510,11 +2124,11 @@ dependencies = [ "axum", "base64", "bytes", - "h2", - "http", - "http-body", + "h2 0.4.7", + "http 1.2.0", + "http-body 1.0.1", "http-body-util", - "hyper", + "hyper 1.6.0", "hyper-timeout", "hyper-util", "percent-encoding", @@ -1540,7 +2154,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1608,7 +2222,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1618,6 +2232,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 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]] @@ -1628,9 +2272,28 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +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" @@ -1638,6 +2301,12 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +[[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" @@ -1654,6 +2323,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "version_check" version = "0.9.5" @@ -1677,9 +2352,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] @@ -1706,7 +2381,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "wasm-bindgen-shared", ] @@ -1728,7 +2403,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1754,6 +2429,22 @@ dependencies = [ "rustix", ] +[[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" @@ -1763,13 +2454,28 @@ 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.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1778,7 +2484,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1787,7 +2493,22 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "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", ] [[package]] @@ -1796,28 +2517,46 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "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", ] +[[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_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_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" @@ -1830,24 +2569,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[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_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_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_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" @@ -1855,14 +2618,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "wit-bindgen-rt" -version = "0.33.0" +name = "winnow" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" +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 = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +dependencies = [ + "is-terminal", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -1890,7 +2671,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1901,7 +2682,7 @@ checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f822433..edbb65b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,11 @@ 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" [build-dependencies] tonic-build = "0.12.3" @@ -43,4 +48,4 @@ tonic-build = "0.12.3" [features] default = ["secure", "auth"] secure = [] -auth = [] \ No newline at end of file +auth = [] diff --git a/macros/Cargo.lock b/macros/Cargo.lock index b79f6d0..a82b7df 100644 --- a/macros/Cargo.lock +++ b/macros/Cargo.lock @@ -31,9 +31,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.109" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", diff --git a/macros/Cargo.toml b/macros/Cargo.toml index a8c2072..e301949 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -10,7 +10,8 @@ edition = "2018" proc-macro = true [dependencies] -quote = "1" -proc-macro2 = "1.0" -syn = "1.0" +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 c4a2c8b..5c08c0e 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,22 +1,105 @@ +mod protos; + extern crate proc_macro; -use proc_macro2::TokenTree; +use proc_macro2::{Ident, Literal, Span, TokenTree}; use proc_macro::TokenStream; +use std::iter::FromIterator; +use std::mem; +use syn::{parse_macro_input, DeriveInput, Data, PathSegment, TraitItem, FieldsNamed, Fields, Visibility, Type, TypePath, Path, ImplItem, ImplItemConst, Expr, ExprLit, Lit, TypeParamBound, TraitBound, TraitBoundModifier, LitInt, Token, FnArg, Receiver, PatType, Pat, TypeInfer, TypeReference, TraitItemFn, Signature, Block, Stmt, Local, LocalInit, LitStr, PathArguments, ReturnType}; +use quote::{quote, ToTokens, TokenStreamExt}; +use syn::buffer::TokenBuffer; +use syn::parse::{Parse, ParseBuffer, ParseStream}; +use syn::punctuated::Punctuated; +use syn::spanned::Spanned; +use syn::token::Comma; +use syn::Visibility::Public; +use crate::protos::{ProtoMethodData, RmcProtocolData}; -use syn::{parse_macro_input, DeriveInput, Data}; -use quote::{quote, TokenStreamExt}; +fn self_referece_type() -> Type { + Type::Reference( + TypeReference { + and_token: Default::default(), + lifetime: None, + mutability: None, + elem: Box::new(Type::Path( + TypePath { + qself: None, + path: Path { + leading_colon: None, + segments: { + let mut punct = Punctuated::new(); + + punct.push_value(PathSegment{ + ident: Ident::new("Self", Span::call_site()), + arguments: PathArguments::None + }); + + punct + } + } + } + )) + } + ) +} + +struct ProtoInputParams{ + proto_num: LitInt, + properties: Option<(Token![,], Punctuated)> +} + +impl Parse for ProtoInputParams{ + fn parse(input: ParseStream) -> syn::Result { + let proto_num = input.parse()?; + + if let Some(seperator) = input.parse()?{ + let mut punctuated = Punctuated::new(); + loop { + punctuated.push_value( + input.parse()? + ); + if let Some(punct) = input.parse()? { + punctuated.push_punct(punct); + } else { + return Ok( + Self{ + proto_num, + properties: Some((seperator, punctuated)) + } + ) + } + } + } else { + Ok( + Self{ + proto_num, + properties: None + } + ) + } + } +} + +fn single_ident_path(ident: Ident) -> Path{ + Path{ + segments: { + let mut punc = Punctuated::new(); + punc.push(PathSegment::from(ident)); + punc + }, + leading_colon: None, + } +} -/// Example of user-defined [derive mode macro][1] -/// -/// [1]: https://doc.rust-lang.org/reference/procedural-macros.html#derive-mode-macros #[proc_macro_derive(RmcSerialize, attributes(extends, rmc_struct))] pub fn rmc_serialize(input: TokenStream) -> TokenStream { let derive_input = parse_macro_input!(input as DeriveInput); let struct_attr = derive_input.attrs.iter() - .find(|a| a.path.segments.len() == 1 && - a.path.segments.first().is_some_and(|p| p.ident.to_string() == "rmc_struct")); + .find(|a| a.path().segments.len() == 1 && + a.path().segments.first().is_some_and(|p| p.ident.to_string() == "rmc_struct")); let Data::Struct(s) = derive_input.data else { panic!("rmc struct type MUST be a struct"); @@ -29,8 +112,8 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { for f in &s.fields{ if f.attrs.iter() - .any(|a| a.path.segments.len() == 1 && - a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends")){ + .any(|a| a.path().segments.len() == 1 && + a.path().segments.first().is_some_and(|p| p.ident.to_string() == "extends")){ continue; } let ident = f.ident.as_ref().unwrap(); @@ -67,8 +150,8 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { for f in &s.fields{ if f.attrs.iter() - .any(|a| a.path.segments.len() == 1 && - a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends")){ + .any(|a| a.path().segments.len() == 1 && + a.path().segments.first().is_some_and(|p| p.ident.to_string() == "extends")){ continue; } @@ -89,23 +172,12 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { // generate base with extends stuff let serialize_base_content = if let Some(attr) = struct_attr{ - let tokens = attr.tokens.clone(); - let token = tokens.into_iter().next().unwrap(); - - let version = match token { - TokenTree::Group(g) => { - match g.stream().into_iter().next().unwrap(){ - TokenTree::Literal(l) => l, - _ => panic!("expected literal") - } - }, - _ => panic!("expected group") - }; + let version: Literal = attr.parse_args().expect("has to be a literal"); let pre_inner = if let Some(f) = s.fields.iter().find(|f| { f.attrs.iter() - .any(|a| a.path.segments.len() == 1 && - a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends")) + .any(|a| a.path().segments.len() == 1 && + a.path().segments.first().is_some_and(|p| p.ident.to_string() == "extends")) }){ let ident= f.ident.as_ref().unwrap(); quote! { @@ -128,22 +200,12 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { }; let deserialize_base_content = if let Some(attr) = struct_attr{ - let tokens = attr.tokens.clone(); - let token = tokens.into_iter().next().unwrap(); + let version: Literal = attr.parse_args().expect("has to be a literal"); - let version = match token { - TokenTree::Group(g) => { - match g.stream().into_iter().next().unwrap(){ - TokenTree::Literal(l) => l, - _ => panic!("expected literal") - } - }, - _ => panic!("expected group") - }; let pre_inner = if let Some(f) = s.fields.iter().find(|f| { f.attrs.iter() - .any(|a| a.path.segments.len() == 1 && - a.path.segments.first().is_some_and(|p| p.ident.to_string() == "extends")) + .any(|a| a.path().segments.len() == 1 && + a.path().segments.first().is_some_and(|p| p.ident.to_string() == "extends")) }){ let ident= f.ident.as_ref().unwrap(); let ty= &f.ty; @@ -181,4 +243,142 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { }; tokens.into() +} + +/// Macro to automatically generate code to use a specific trait as an rmc protocol for calling to +/// remote objects or accepting incoming remote requests. +/// This is needed in order to be able to use this as part of an rmc server interface. +/// +/// The protocol id which is needed to be specified is specified as a parameter to this attribute. +/// +/// You will also need to assign each function inside the trait a method id by using the +/// [`macro@method_id`] attribute. +/// +/// You can also specify to have the protocol to be non-returning by adding a second parameter to +/// the attribute which is just `NoReturn` e.g. ` #[rmc_proto(1, NoReturn)]` +/// +/// Example +/// ``` +/// // this rmc protocol has protocol id 1 +/// use macros::rmc_proto; +/// +/// #[rmc_proto(1)] +/// trait ExampleProtocol{ +/// // this defines an rmc method with id 1 +/// #[rmc_method(1)] +/// async fn hello_world_method(&self, name: String) -> Result; +/// } +/// ``` +#[proc_macro_attribute] +pub fn rmc_proto(attr: TokenStream, input: TokenStream) -> TokenStream{ + + let mut params = parse_macro_input!(attr as ProtoInputParams); + + let ProtoInputParams{ + proto_num, + properties + } = params; + + let no_return_data = properties.is_some_and(|p| p.1.iter().any(|i|{ + i.to_string() == "NoReturn" + })); + + let mut input = parse_macro_input!(input as syn::ItemTrait); + + // gigantic ass struct initializer (to summarize this gets all of the data) + let raw_data = RmcProtocolData{ + has_returns: !no_return_data, + name: input.ident.clone(), + id: proto_num, + methods: input + .items + .iter() + .filter_map(|v| match v{ TraitItem::Fn(v) => Some(v), _ => None }) + .map(|func|{ + let Some(attr) = func.attrs.iter() + .find(|a| a.path().segments.last().is_some_and(|s| s.ident.to_string() == "method_id")) else { + panic!( "every function inside of an rmc protocol must have a method id"); + }; + + let Ok(id): Result = attr.parse_args() else { + panic!("todo: put a propper error message here"); + }; + + let funcs = func + .sig + .inputs + .iter() + .skip(1) + .map(|f| { + let FnArg::Typed(t) = f else { + panic!("what"); + }; + let Pat::Ident(i) = &*t.pat else { + panic!("unable to handle non identifier patterns as parameter bindings"); + }; + + (i.ident.clone(), t.ty.as_ref().clone()) + }).collect(); + + ProtoMethodData{ + id, + name: func.sig.ident.clone(), + parameters: funcs, + ret_val: func.sig.output.clone() + } + }).collect() + + }; + + quote!{ + #input + #raw_data + }.into() +} + +/// Used to specify the method id of methods when making rmc protocols. +/// See [`macro@rmc_proto`] for further details. +/// +/// Note: This attribute doesn't do anything by itself and just returns the thing it was attached to +/// unchanged. +#[proc_macro_attribute] +pub fn method_id(_attr: TokenStream, input: TokenStream) -> TokenStream{ + // this attribute doesnt do anything by itself, see `rmc_proto` + input +} + + + +#[proc_macro_attribute] +pub fn rmc_struct(attr: TokenStream, input: TokenStream) -> TokenStream{ + let mut type_data = parse_macro_input!(input as DeriveInput); + let mut ident = parse_macro_input!(attr as syn::Path); + let last_token = ident.segments.last_mut().expect("empty path?"); + + last_token.ident = Ident::new(&("Local".to_owned() + &last_token.ident.to_string()), last_token.span()); + + + let struct_name = &type_data.ident; + + let out = quote!{ + #type_data + + impl #ident for #struct_name{ + + } + + impl crate::rmc::protocols::RmcCallable for #struct_name{ + async fn rmc_call(&self, remote_response_connection: &crate::prudp::socket::SendingConnection, 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; + } + } + }; + + out.into() +} + +#[proc_macro_attribute] +pub fn connection(_attr: TokenStream, input: TokenStream) -> TokenStream{ + // this attribute doesnt do anything by itself, see `rmc_struct` + input } \ No newline at end of file diff --git a/macros/src/protos.rs b/macros/src/protos.rs new file mode 100644 index 0000000..554df22 --- /dev/null +++ b/macros/src/protos.rs @@ -0,0 +1,291 @@ +use proc_macro2::{Ident, Span, TokenStream, TokenTree}; +use quote::{quote, ToTokens}; +use syn::{LitInt, ReturnType, Token, Type}; +use syn::token::{Brace, Paren, Semi}; + +pub struct ProtoMethodData{ + pub id: LitInt, + pub name: Ident, + pub parameters: Vec<(Ident, Type)>, + pub ret_val: ReturnType, +} + + +/// This is a representation of the code generated by `rmc_proto` it serves to split the logic of +/// acquiring data from the actual generation to tidy up the process into first getting then +/// generating. +/// +/// Use the [`ToTokens`] trait to generate the actual code. +pub struct RmcProtocolData{ + pub has_returns: bool, + pub id: LitInt, + pub name: Ident, + pub methods: Vec +} + +impl RmcProtocolData{ + fn generate_raw_trait(&self, tokens: &mut TokenStream){ + let Self{ + has_returns, + name, + id, + methods + } = self; + + // this gives us the name which the identifier of the corresponding Raw trait + let raw_name = Ident::new(&format!("Raw{}", name), name.span()); + + + // boilerplate tokens which all raw traits need + quote!{ + #[doc(hidden)] + pub trait #raw_name: #name + }.to_tokens(tokens); + + // generate the body of the raw protocol trait + Brace::default().surround(tokens, |tokens|{ + //generate each raw method + for method in methods{ + let ProtoMethodData { + name, + parameters, + .. + } = method; + + let raw_name = Ident::new(&format!("raw_{}", name), name.span()); + quote!{ + async fn #raw_name + }.to_tokens(tokens); + + Paren::default().surround(tokens, |tokens|{ + quote!{ &self, data: ::std::vec::Vec }.to_tokens(tokens); + }); + + quote!{ + -> ::core::result::Result, ErrorCode> + }.to_tokens(tokens); + + Brace::default().surround(tokens, |tokens|{ + quote! { let mut cursor = ::std::io::Cursor::new(data); }.to_tokens(tokens); + + for (param_name, param_type) in parameters{ + quote!{ + let Ok(#param_name) = + <#param_type as crate::rmc::structures::RmcSerialize>::deserialize( + &mut cursor + ) else { + return Err(crate::rmc::response::ErrorCode::Core_InvalidArgument); + }; + }.to_tokens(tokens) + } + + quote!{ + let retval = self.#name + }.to_tokens(tokens); + + Paren::default().surround(tokens, |tokens|{ + for (paren_name, _) in parameters{ + quote!{#paren_name,}.to_tokens(tokens); + } + }); + + quote!{ + .await; + }.to_tokens(tokens); + + if *has_returns{ + quote!{ + let retval = retval?; + let mut vec = Vec::new(); + crate::rmc::structures::RmcSerialize::serialize(&retval, &mut vec).ok(); + Ok(vec) + }.to_tokens(tokens); + } + }) + } + + quote!{ + async fn rmc_call_proto( + &self, + remote_response_connection: &crate::prudp::socket::SendingConnection, + method_id: u32, + call_id: u32, + data: Vec, + ) + }.to_tokens(tokens); + + Brace::default().surround(tokens, |tokens|{ + quote! { + let ret = match method_id + }.to_tokens(tokens); + + Brace::default().surround(tokens, |tokens|{ + for method in methods{ + let ProtoMethodData{ + id, + name, + .. + } = method; + + let raw_name = Ident::new(&format!("raw_{}", name), name.span()); + + quote!{ + #id => self.#raw_name(data).await, + }.to_tokens(tokens); + } + quote!{ + _ => Err(crate::rmc::response::ErrorCode::Core_NotImplemented) + }.to_tokens(tokens); + }); + + Semi::default().to_tokens(tokens); + + if *has_returns{ + quote!{ + crate::rmc::response::send_result( + remote_response_connection, + ret, + #id, + method_id, + call_id, + ).await + }.to_tokens(tokens); + } + }); + }); + + quote!{ + impl RawAuth for T{} + }.to_tokens(tokens); + } + + fn generate_raw_remote_trait(&self, tokens: &mut TokenStream) { + let Self { + has_returns, + name, + id: proto_id, + methods, + .. + } = self; + + // this gives us the name which the identifier of the corresponding Raw trait + let remote_name = Ident::new(&format!("Remote{}", name), name.span()); + + + // boilerplate tokens which all raw traits need + quote!{ + #[doc(hidden)] + pub trait #remote_name: crate::rmc::protocols::HasRmcConnection + }.to_tokens(tokens); + + // generate the body of the raw protocol trait + Brace::default().surround(tokens, |tokens|{ + //generate each raw method + for method in methods{ + let ProtoMethodData { + name, + parameters, + ret_val, + id: method_id, + .. + } = method; + + quote!{ + async fn #name + }.to_tokens(tokens); + + Paren::default().surround(tokens, |tokens|{ + quote!{ &self, }.to_tokens(tokens); + for (param_ident, param_type) in parameters{ + quote!{ #param_ident: #param_type, }.to_tokens(tokens); + } + }); + + quote!{ + #ret_val + }.to_tokens(tokens); + + Brace::default().surround(tokens, |tokens|{ + quote! { + let mut send_data = Vec::new(); + let mut cursor = ::std::io::Cursor::new(&mut send_data); + }.to_tokens(tokens); + + for (param_name, param_type) in parameters{ + quote!{ + crate::result::ResultExtension::display_err_or_some( + <#param_type as crate::rmc::structures::RmcSerialize>::serialize( + &#param_name, + &mut cursor + ) + ).ok_or(crate::rmc::response::ErrorCode::Core_InvalidArgument)?; + }.to_tokens(tokens) + } + + quote!{ + let call_id = rand::random(); + + let message = crate::rmc::message::RMCMessage{ + call_id, + method_id: #method_id, + protocol_id: #proto_id, + rest_of_data: send_data + }; + + let rmc_conn = ::get_connection(self); + }.to_tokens(tokens); + + if *has_returns{ + quote!{ + crate::result::ResultExtension::display_err_or_some( + rmc_conn.make_raw_call(&message).await + ).ok_or(crate::rmc::response::ErrorCode::Core_Exception) + }.to_tokens(tokens); + } else { + quote!{ + crate::result::ResultExtension::display_err_or_some( + rmc_conn.make_raw_call_no_response(&message).await + ); + }.to_tokens(tokens); + } + + }) + } + }); + + + } + + fn generate_raw_info(&self, tokens: &mut TokenStream){ + let Self{ + name, + id, + .. + } = self; + + let raw_info_name = Ident::new(&format!("Raw{}Info", name), Span::call_site()); + + + quote!{ + #[doc(hidden)] + pub struct #raw_info_name; + + impl #raw_info_name { + pub const PROTOCOL_ID: u16 = #id; + } + }.to_tokens(tokens); + } +} + +impl ToTokens for RmcProtocolData{ + fn to_tokens(&self, tokens: &mut TokenStream) { + self.generate_raw_trait(tokens); + self.generate_raw_info(tokens); + self.generate_raw_remote_trait(tokens); + + + + + } +} + diff --git a/src/grpc/mod.rs b/src/grpc/mod.rs index d1a4342..2ca742e 100644 --- a/src/grpc/mod.rs +++ b/src/grpc/mod.rs @@ -1,3 +1,7 @@ +//! Legacy grpc communication server for being able to use this with pretendos infrastructure +//! before account rs is finished. +//! +//! This WILL be deprecated as soon as account rs is in a stable state. use tonic::{Request, Status}; type InterceptorFunc = Box<(dyn Fn(Request<()>) -> Result, Status> + Send)>; diff --git a/src/kerberos/mod.rs b/src/kerberos/mod.rs index 8fb46fa..a40b816 100644 --- a/src/kerberos/mod.rs +++ b/src/kerberos/mod.rs @@ -25,7 +25,7 @@ pub fn derive_key(pid: u32, password: [u8; 16]) -> [u8; 16]{ key } -#[derive(Pod, Zeroable, Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Pod, Zeroable, Copy, Clone, Debug, Eq, PartialEq, Default)] #[repr(transparent)] pub struct KerberosDateTime(pub u64); @@ -66,7 +66,7 @@ impl KerberosDateTime{ #[inline] pub fn get_month(&self) -> u8{ - ((self.0 >> 22) & 0b111111) as u8 + ((self.0 >> 22) & 0b1111) as u8 } #[inline] @@ -154,3 +154,17 @@ impl Ticket{ data.into_boxed_slice() } } + + +#[cfg(test)] +mod test{ + use chrono::{Datelike, Utc}; + use crate::kerberos::KerberosDateTime; + + #[test] + fn kerberos_time_convert_test(){ + let time = KerberosDateTime(135904948834); + + println!("{}", time.to_regular_time().to_rfc2822()); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index b2d7765..08f3b87 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,104 +1,127 @@ #![allow(dead_code)] +#![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. -use std::{env, fs}; -use std::collections::BTreeMap; -use std::fs::File; -use std::net::{Ipv4Addr, SocketAddrV4}; -use std::sync::Arc; -use chrono::{Local, SecondsFormat}; -use log::info; -use once_cell::sync::Lazy; -use rc4::{KeyInit, Rc4, StreamCipher}; -use rc4::consts::U5; -use simplelog::{ColorChoice, CombinedLogger, Config, LevelFilter, TerminalMode, TermLogger, WriteLogger}; -use tokio::sync::RwLock; -use tokio::task::JoinHandle; use crate::nex::account::Account; -use crate::protocols::{auth, block_if_maintenance}; -use crate::protocols::auth::AuthProtocolConfig; -use crate::protocols::matchmake_common::MatchmakeData; -use crate::protocols::server::RMCProtocolServer; -use crate::prudp::socket::{ActiveSecureConnectionData, EncryptionPair, Socket}; -use crate::prudp::packet::{VirtualPort}; +use crate::nex::auth_handler::{AuthHandler, RemoteAuthClientProtocol}; +use crate::prudp::packet::VirtualPort; use crate::prudp::router::Router; -use crate::prudp::secure::{generate_secure_encryption_pairs, read_secure_connection_data}; -use crate::rmc::message::RMCMessage; -use crate::rmc::structures::RmcSerialize; +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::{new_rmc_gateway_connection, OnlyRemote}; +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 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::time::Duration; +use std::{env, fs}; +use tokio::task::JoinHandle; +use crate::nex::user::User; mod endianness; mod prudp; pub mod rmc; -mod protocols; +//mod protocols; -mod nex; mod grpc; mod kerberos; +mod nex; +mod result; +mod versions; +mod web; -static KERBEROS_SERVER_PASSWORD: Lazy = Lazy::new(||{ +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_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(||{ +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(||{ +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(||{ +static OWN_IP_PRIVATE: Lazy = Lazy::new(|| { env::var("SERVER_IP") .ok() .and_then(|s| s.parse().ok()) .expect("no public ip specified") }); -static OWN_IP_PUBLIC: Lazy = Lazy::new(||{ - env::var("SERVER_IP_PUBLIC") - .ok() - .and_then(|s| s.parse().ok()) - .unwrap_or(*OWN_IP_PRIVATE) -}); +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 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 + ) +}); #[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(); + 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(); dotenv::dotenv().ok(); start_servers().await; } +/* struct AuthServer{ router: Arc, @@ -194,7 +217,8 @@ async fn start_secure_server() -> SecureServer{ 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::matchmake_extension::bound_protocol(matchmake_data)), + Box::new(protocols::nat_traversal::bound_protocol()) ])); let socket = @@ -237,74 +261,116 @@ async fn start_secure_server() -> SecureServer{ 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(); + + loop { + let Some(conn) = socket_secure.accept().await else { + error!("server crashed"); + return; + }; + + info!("new connected user!"); + + let _ = new_rmc_gateway_connection(conn, |_| 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_servers(){ +async fn start_secure() -> JoinHandle<()> { + tokio::spawn(async { + 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(); + + loop { + let Some(conn) = socket_secure.accept().await else { + error!("server crashed"); + return; + }; + + info!("new connected user on secure :D!"); + + let ip = conn.socket_addr.regular_socket_addr; + let pid = conn.user_id; + + let _ = new_rmc_gateway_connection(conn, |_| User { + ip, + pid + }); + } + }) +} + +async fn start_test() { + let addr = SocketAddrV4::new(*OWN_IP_PRIVATE, *AUTH_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| OnlyRemote::::new(r)); + + let v = remote + .login_ex("1469690705".to_string(), Any::default()) + .await + .unwrap(); + + println!("got it"); +} + +async fn start_servers() { + #[cfg(feature = "auth")] + let auth_server = start_auth().await; + #[cfg(feature = "secure")] + let secure_server = start_secure().await; + //let web_server = web::start_web().await; + + //tokio::time::sleep(Duration::from_secs(1)).await; + + //start_test().await; #[cfg(feature = "auth")] - let auth_server = start_auth_server().await; + auth_server.await.expect("auth server crashed"); #[cfg(feature = "secure")] - let secure_server = start_secure_server().await; - - #[cfg(feature = "auth")] - auth_server.join_handle.await.expect("auth server crashed"); - #[cfg(feature = "secure")] - secure_server.join_handle.await.expect("auth server crashed"); -} - - -#[cfg(test)] -mod test{ - use std::io::Cursor; - use std::num::ParseIntError; - use std::str::from_utf8; - use hmac::digest::consts::U5; - use rc4::{KeyInit, Rc4, StreamCipher}; - use crate::prudp::packet::PRUDPPacket; - use crate::rmc; - - fn from_hex_stream(val: &str) -> Result, ParseIntError> { - let res: Result, _> = val.as_bytes() - .chunks_exact(2) - .map(|c| from_utf8(c).expect("unable to convert back to string")) - .map(|s| u8::from_str_radix(s, 16)) - .collect(); - - res - } - - #[tokio::test] - async fn simulate_packets(){ - let val = from_hex_stream("ead001037d00afa1e200a5000200d9e4a4050368c18c6de4e2fb1cc40f0c020100768744db99f92c5005a061fd2a1df280cd64d5c1a565952c6befa607cbaf34661312b16db0fa6fccfb81e28b5a3a9bed02b49152bbc99cc112b7e29b9e45ec3d4b89df0fe71390883d9a927c264d07ada0de9cd28499e3ccdf3fd079e4a9848d4d783778c42da2af06106a7326634dc5bec5c3438ef18e30109839ffcc").expect("uuuuh"); - - let mut packet = PRUDPPacket::new(&mut Cursor::new(&val)).expect("invalid packet"); - - let mut rc4: Rc4 = - Rc4::new_from_slice("CD&ML".as_bytes().into()).expect("invalid key"); - - rc4.apply_keystream(&mut packet.payload); - - println!("packet: {:?}", packet); - - let rmc_packet = rmc::message::RMCMessage::new(&mut Cursor::new(&packet.payload)).expect("unable to read message"); - - let mut a = Cursor::new(&rmc_packet.rest_of_data); - - //let pid = rmc::structures::string::read(&mut a).expect("unable to read pid"); - } - - #[tokio::test] - async fn simulate_packets_response(){ - let val = from_hex_stream("ead001032501a1af6200a500010013ffcdbc3a2ebc44efc6e38ea32a72b40201002e8644db19fe2a5005a2637d2a16f3b1fe5633037c1ed61c5aefad8afebdf2ff8600e9350fba1298b570c70f6dd647eac2d3faf0ab74ef761e2ee43dc10e249e5f91aed6813dcc04b3c707d9442b6e353b9b0b654e98f860fe5379c41d3c2a1874b7dd37ebf499e03bd2fd3e9a9203c0959feb760c38f504dcd0c9e99b17fd410657da4efa3e01c8a68ab3042d6d489788d5580778d32249cdf1fba8bf68cf4019d116ea7c580622ea1e3635139d91b44635d5e95b6c35b33898fdc0117fa6fc7162840d07a49f1e7089aa0ea65409a8ddeb2334449ba73a0ff7de462cf4a706a696de0f0521b84ae5a3f8587f3585d202d3cc0fb0451519c1b830b5e3cdd6de52e9add7325cbbf08a7c2f8b875934942b226703a22b4bc8931932dab055049051e4144b02").expect("uuuuh"); - - let mut packet = PRUDPPacket::new(&mut Cursor::new(&val)).expect("invalid packet"); - - let mut rc4: Rc4 = - Rc4::new_from_slice("CD&ML".as_bytes().into()).expect("invalid key"); - - rc4.apply_keystream(&mut packet.payload); - - println!("packet: {:?}", packet); - } + secure_server.await.expect("auth server crashed"); + //web_server.await.expect("webserver crashed"); } diff --git a/src/protocols/auth/method_login.rs b/src/nex-implementation/auth/method_login.rs similarity index 93% rename from src/protocols/auth/method_login.rs rename to src/nex-implementation/auth/method_login.rs index d2c1642..aface70 100644 --- a/src/protocols/auth/method_login.rs +++ b/src/nex-implementation/auth/method_login.rs @@ -3,7 +3,6 @@ use std::sync::Arc; use log::error; use tokio::sync::Mutex; use crate::protocols::auth::AuthProtocolConfig; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponseResult}; use crate::rmc::structures::RmcSerialize; diff --git a/src/protocols/auth/method_login_ex.rs b/src/nex-implementation/auth/method_login_ex.rs similarity index 98% rename from src/protocols/auth/method_login_ex.rs rename to src/nex-implementation/auth/method_login_ex.rs index 63ff73f..413ab82 100644 --- a/src/protocols/auth/method_login_ex.rs +++ b/src/nex-implementation/auth/method_login_ex.rs @@ -7,7 +7,6 @@ use crate::grpc::account; use crate::kerberos::KerberosDateTime; use crate::protocols::auth::AuthProtocolConfig; use crate::protocols::auth::ticket_generation::generate_ticket; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponseResult}; diff --git a/src/protocols/auth/method_request_ticket.rs b/src/nex-implementation/auth/method_request_ticket.rs similarity index 97% rename from src/protocols/auth/method_request_ticket.rs rename to src/nex-implementation/auth/method_request_ticket.rs index 6f2d767..aa89448 100644 --- a/src/protocols/auth/method_request_ticket.rs +++ b/src/nex-implementation/auth/method_request_ticket.rs @@ -4,7 +4,6 @@ use tokio::sync::Mutex; use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; use crate::protocols::auth::{AuthProtocolConfig, get_login_data_by_pid}; use crate::protocols::auth::ticket_generation::generate_ticket; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponseResult}; use crate::rmc::response::ErrorCode::Core_Unknown; diff --git a/src/protocols/auth/mod.rs b/src/nex-implementation/auth/mod.rs similarity index 100% rename from src/protocols/auth/mod.rs rename to src/nex-implementation/auth/mod.rs diff --git a/src/protocols/auth/ticket_generation.rs b/src/nex-implementation/auth/ticket_generation.rs similarity index 100% rename from src/protocols/auth/ticket_generation.rs rename to src/nex-implementation/auth/ticket_generation.rs diff --git a/src/protocols/matchmake/method_unregister_gathering.rs b/src/nex-implementation/matchmake/method_unregister_gathering.rs similarity index 95% rename from src/protocols/matchmake/method_unregister_gathering.rs rename to src/nex-implementation/matchmake/method_unregister_gathering.rs index d055afb..22ec79f 100644 --- a/src/protocols/matchmake/method_unregister_gathering.rs +++ b/src/nex-implementation/matchmake/method_unregister_gathering.rs @@ -2,7 +2,6 @@ use std::io::Cursor; use std::sync::Arc; use tokio::sync::{Mutex, RwLock}; use crate::protocols::matchmake_common::MatchmakeData; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponseResult}; use crate::rmc::structures::qresult::QResult; diff --git a/src/protocols/matchmake/mod.rs b/src/nex-implementation/matchmake/mod.rs similarity index 100% rename from src/protocols/matchmake/mod.rs rename to src/nex-implementation/matchmake/mod.rs diff --git a/src/nex-implementation/matchmake_common/mod.rs b/src/nex-implementation/matchmake_common/mod.rs new file mode 100644 index 0000000..c4265d3 --- /dev/null +++ b/src/nex-implementation/matchmake_common/mod.rs @@ -0,0 +1,136 @@ +use std::collections::{BTreeMap}; +use std::sync::Arc; +use log::error; +use rand::random; +use tokio::sync::{Mutex, RwLock}; +use crate::kerberos::KerberosDateTime; +use crate::protocols::notification::Notification; +use crate::rmc::structures::matchmake::{Gathering, MatchmakeParam, MatchmakeSession}; +use crate::rmc::structures::variant::Variant; + +#[derive(Default, Debug)] +pub struct ExtendedMatchmakeSession{ + pub session: MatchmakeSession, + pub connected_players: Vec>>, +} + +pub struct MatchmakeData{ + pub(crate) matchmake_sessions: BTreeMap>> +} + +impl ExtendedMatchmakeSession{ + pub async fn from_matchmake_session(session: MatchmakeSession, host: &Mutex) -> Self{ + let host = host.lock().await; + + let ConnectionData{ + active_connection_data, + .. + } = &*host; + + let Some(active_connection_data) = active_connection_data else{ + return Default::default(); + }; + + let ActiveConnectionData{ + active_secure_connection_data, + .. + } = active_connection_data; + + let Some(active_secure_connection_data) = active_secure_connection_data else{ + return Default::default(); + }; + + + let mm_session = MatchmakeSession{ + gathering: Gathering{ + self_gid: 1, + owner_pid: active_secure_connection_data.pid, + host_pid: active_secure_connection_data.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_player(&mut self, socket: &SocketData, conn: Arc>, join_msg: String) { + let locked = conn.lock().await; + + let Some(joining_pid) = locked.active_connection_data.as_ref() + .map(|c| + c.active_secure_connection_data.as_ref() + .map(|c| c.pid) + ).flatten() else { + error!("tried to add player without secure connection"); + return + }; + + drop(locked); + + self.connected_players.push(conn); + self.session.participation_count = self.connected_players.len() as u32; + + + for other_connection in &self.connected_players{ + let mut conn = other_connection.lock().await; + + + let Some(other_pid) = conn.active_connection_data.as_ref() + .map(|c| + c.active_secure_connection_data.as_ref() + .map(|c| c.pid + ) + ).flatten() else { + error!("tried to send connection notification to player secure connection"); + return + }; + + /*if other_pid == self.session.gathering.owner_pid && + joining_pid == self.session.gathering.owner_pid{ + continue; + }*/ + + conn.send_notification(socket, Notification{ + pid_source: joining_pid, + notif_type: 3001, + param_1: self.session.gathering.self_gid, + param_2: other_pid, + str_param: join_msg.clone(), + param_3: self.session.participation_count + }).await; + } + } +} +pub async fn add_matchmake_session(mm_data: Arc>,session: ExtendedMatchmakeSession) -> Arc> { + let gid = session.session.gathering.self_gid; + + let mut mm_data = mm_data.write().await; + + let session = Arc::new(Mutex::new(session)); + + mm_data.matchmake_sessions.insert(gid, session.clone()); + + session +} + +impl MatchmakeData { + + + + pub async fn try_find_session_with_criteria(&self, ) -> Option>>{ + None + } +} \ No newline at end of file diff --git a/src/protocols/matchmake_extension/method_auto_matchmake_with_param_postpone.rs b/src/nex-implementation/matchmake_extension/method_auto_matchmake_with_param_postpone.rs similarity index 65% rename from src/protocols/matchmake_extension/method_auto_matchmake_with_param_postpone.rs rename to src/nex-implementation/matchmake_extension/method_auto_matchmake_with_param_postpone.rs index 2d53080..4c5d70a 100644 --- a/src/protocols/matchmake_extension/method_auto_matchmake_with_param_postpone.rs +++ b/src/nex-implementation/matchmake_extension/method_auto_matchmake_with_param_postpone.rs @@ -1,12 +1,15 @@ use std::io::Cursor; use std::sync::Arc; +use std::time::Duration; +use chrono::SecondsFormat::Millis; +use log::info; use rand::random; use tokio::sync::{Mutex, RwLock}; +use tokio::time::sleep; use crate::protocols::matchmake_common::{ExtendedMatchmakeSession, MatchmakeData}; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponseResult}; -use crate::rmc::structures::matchmake::{AutoMatchmakeParam}; +use crate::rmc::structures::matchmake::{AutoMatchmakeParam, MatchmakeSession}; use crate::rmc::structures::RmcSerialize; @@ -18,7 +21,7 @@ pub async fn auto_matchmake_with_param_postpone( mm_data: Arc>, auto_matchmake_param: AutoMatchmakeParam ) -> RMCResponseResult{ - println!("auto_matchmake_with_param_postpone: {:?}", auto_matchmake_param); + //println!("auto_matchmake_with_param_postpone: {:?}", auto_matchmake_param); let locked_conn = conn.lock().await; let Some(secure_conn) = locked_conn.active_connection_data.as_ref().map(|a| a.active_secure_connection_data.as_ref()).flatten() else { @@ -38,35 +41,41 @@ pub async fn auto_matchmake_with_param_postpone( // up anything else unnescesarily drop(mm_data_read); - let gid = random(); - - let mut matchmake_session = auto_matchmake_param.matchmake_session.clone(); - matchmake_session.gathering.self_gid = gid; - matchmake_session.gathering.host_pid = pid; - matchmake_session.gathering.owner_pid = pid; - + let session = + ExtendedMatchmakeSession::from_matchmake_session(auto_matchmake_param.matchmake_session, &conn).await; + let gid = session.session.gathering.self_gid; let mut mm_data = mm_data.write().await; - let session = Arc::new(Mutex::new(ExtendedMatchmakeSession{ - session: matchmake_session.clone(), - connected_players: Vec::new() - })); + let session = Arc::new(Mutex::new(session)); mm_data.matchmake_sessions.insert(gid, session.clone()); session }; - let mut session = session.lock().await; + let mut locked_session = session.lock().await; //todo: refactor so that this works - session.add_player(socket, conn.clone(), auto_matchmake_param.join_message).await; + { + let session = session.clone(); + let socket = socket.clone(); + let connection = conn.clone(); + let join_msg = auto_matchmake_param.join_message.clone(); + tokio::spawn(async move{ + sleep(Duration::from_millis(500)).await; + println!("adding player"); + let mut session = session.lock().await; + session.add_player(&socket, connection, join_msg).await; + }); + } + + info!("new session: {:?}", locked_session); let mut response = Vec::new(); - session.session.serialize(&mut response).expect("unable to serialize matchmake session"); + locked_session.session.serialize(&mut response).expect("unable to serialize matchmake session"); rmcmessage.success_with_data(response) } diff --git a/src/nex-implementation/matchmake_extension/method_create_matchmake_session_with_param.rs b/src/nex-implementation/matchmake_extension/method_create_matchmake_session_with_param.rs new file mode 100644 index 0000000..438a9ed --- /dev/null +++ b/src/nex-implementation/matchmake_extension/method_create_matchmake_session_with_param.rs @@ -0,0 +1,87 @@ +use std::io::Cursor; +use std::sync::Arc; +use std::time::Duration; +use log::info; +use tokio::sync::{Mutex, RwLock}; +use tokio::time::sleep; +use crate::protocols::matchmake_common::{add_matchmake_session, ExtendedMatchmakeSession, MatchmakeData}; +use crate::protocols::matchmake_extension::method_auto_matchmake_with_param_postpone::auto_matchmake_with_param_postpone; +use crate::rmc::message::RMCMessage; +use crate::rmc::response::{ErrorCode, RMCResponseResult}; +use crate::rmc::structures::matchmake::{AutoMatchmakeParam, CreateMatchmakeSessionParam}; +use crate::rmc::structures::RmcSerialize; + +pub async fn create_matchmake_session_with_param( + rmcmessage: &RMCMessage, + conn: &Arc>, + socket: &Arc, + mm_data: Arc>, + create_matchmake_session: CreateMatchmakeSessionParam +) -> RMCResponseResult { + + let mut session = + ExtendedMatchmakeSession::from_matchmake_session(create_matchmake_session.matchmake_session, &conn).await; + + session.session.participation_count = create_matchmake_session.participation_count as u32; + + let session = add_matchmake_session(mm_data, session).await; + + let mut session = session.lock().await; + + session.add_player(&socket, conn.clone(), create_matchmake_session.join_message).await; + + + + let mut response = Vec::new(); + + + session.session.serialize(&mut response).expect("unable to serialize session"); + + println!("{}", hex::encode(&response)); + + + + rmcmessage.success_with_data(response) +} + +pub async fn create_matchmake_session_with_param_raw_params( + rmcmessage: &RMCMessage, + socket: &Arc, + connection_data: &Arc>, + data: Arc> +) -> RMCResponseResult{ + let mut reader = Cursor::new(&rmcmessage.rest_of_data); + + let Ok(matchmake_param) = CreateMatchmakeSessionParam::deserialize(&mut reader) else { + return rmcmessage.error_result_with_code(ErrorCode::Core_InvalidArgument); + }; + + create_matchmake_session_with_param(rmcmessage, connection_data, socket, data, matchmake_param).await +} + +#[cfg(test)] +mod test{ + use std::io::Cursor; + use crate::prudp::packet::PRUDPPacket; + use crate::rmc::message::RMCMessage; + use crate::rmc::structures::matchmake::MatchmakeSession; + use crate::rmc::structures::RmcSerialize; + + #[test] + fn test(){ + let data = hex::decode("ead001030000a1af12001800050002010000000000000000000000000000000000").unwrap(); + + let packet = PRUDPPacket::new(&mut Cursor::new(data)).unwrap(); + + println!("{:?}", packet); + } + + #[test] + fn test_2(){ + let data = hex::decode("250000008e0100000001000000001700000051b39957b90b00000100000051b3995701000001000000").unwrap(); + + let msg = RMCMessage::new(&mut Cursor::new(data)).unwrap(); + + println!("{:?}", msg) + } +} \ No newline at end of file diff --git a/src/protocols/matchmake_extension/method_get_playing_session.rs b/src/nex-implementation/matchmake_extension/method_get_playing_session.rs similarity index 95% rename from src/protocols/matchmake_extension/method_get_playing_session.rs rename to src/nex-implementation/matchmake_extension/method_get_playing_session.rs index bc4eb3c..d88a6f5 100644 --- a/src/protocols/matchmake_extension/method_get_playing_session.rs +++ b/src/nex-implementation/matchmake_extension/method_get_playing_session.rs @@ -3,7 +3,6 @@ use std::sync::Arc; use log::info; use tokio::sync::{Mutex, RwLock}; use crate::protocols::matchmake_common::MatchmakeData; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponseResult}; use crate::rmc::structures::RmcSerialize; diff --git a/src/protocols/matchmake_extension/mod.rs b/src/nex-implementation/matchmake_extension/mod.rs similarity index 69% rename from src/protocols/matchmake_extension/mod.rs rename to src/nex-implementation/matchmake_extension/mod.rs index 509f3bb..ddca083 100644 --- a/src/protocols/matchmake_extension/mod.rs +++ b/src/nex-implementation/matchmake_extension/mod.rs @@ -1,5 +1,6 @@ mod method_get_playing_session; mod method_auto_matchmake_with_param_postpone; +mod method_create_matchmake_session_with_param; use std::sync::Arc; use tokio::sync::{RwLock}; @@ -7,10 +8,12 @@ use crate::define_protocol; use crate::protocols::matchmake_common::MatchmakeData; use method_get_playing_session::get_playing_session_raw_params; use method_auto_matchmake_with_param_postpone::auto_matchmake_with_param_postpone_raw_params; +use crate::protocols::matchmake_extension::method_create_matchmake_session_with_param::create_matchmake_session_with_param_raw_params; define_protocol!{ 109(matchmake_data: Arc>) => { 16 => get_playing_session_raw_params, + 38 => create_matchmake_session_with_param_raw_params, 40 => auto_matchmake_with_param_postpone_raw_params } } \ No newline at end of file diff --git a/src/protocols/mod.rs b/src/nex-implementation/mod.rs similarity index 98% rename from src/protocols/mod.rs rename to src/nex-implementation/mod.rs index 89916af..16bf0bf 100644 --- a/src/protocols/mod.rs +++ b/src/nex-implementation/mod.rs @@ -6,7 +6,6 @@ use log::warn; use once_cell::sync::Lazy; use tokio::sync::Mutex; use crate::grpc; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::response::{ErrorCode, RMCResponse}; @@ -17,7 +16,8 @@ pub mod secure; pub mod matchmake_extension; pub mod matchmake_common; pub mod matchmake; -mod notification; +pub mod notification; +pub mod nat_traversal; static IS_MAINTENANCE: Lazy = Lazy::new(|| { env::var("IS_MAINTENANCE") diff --git a/src/nex-implementation/nat_traversal/method_report_nat_properties.rs b/src/nex-implementation/nat_traversal/method_report_nat_properties.rs new file mode 100644 index 0000000..7d01740 --- /dev/null +++ b/src/nex-implementation/nat_traversal/method_report_nat_properties.rs @@ -0,0 +1,29 @@ +use std::io::Cursor; +use std::sync::Arc; +use std::time::Duration; +use tokio::sync::{Mutex, RwLock}; +use tokio::time::sleep; +use crate::protocols::matchmake_common::MatchmakeData; +use crate::rmc::message::RMCMessage; +use crate::rmc::response::{ErrorCode, RMCResponseResult}; +use crate::rmc::structures::matchmake::CreateMatchmakeSessionParam; + +pub async fn report_nat_properties( + rmcmessage: &RMCMessage, + socket: &Arc, + connection_data: &Arc>, +) -> RMCResponseResult{ + sleep(Duration::from_millis(50)).await; + rmcmessage.success_with_data(Vec::new()) +} + +pub async fn report_nat_properties_raw_params( + rmcmessage: &RMCMessage, + socket: &Arc, + connection_data: &Arc>, + _: () +) -> RMCResponseResult{ + let mut reader = Cursor::new(&rmcmessage.rest_of_data); + + report_nat_properties(rmcmessage, socket, connection_data).await +} \ No newline at end of file diff --git a/src/nex-implementation/nat_traversal/mod.rs b/src/nex-implementation/nat_traversal/mod.rs new file mode 100644 index 0000000..48e59e6 --- /dev/null +++ b/src/nex-implementation/nat_traversal/mod.rs @@ -0,0 +1,10 @@ +mod method_report_nat_properties; + +use crate::define_protocol; +use crate::protocols::nat_traversal::method_report_nat_properties::report_nat_properties_raw_params; + +define_protocol!{ + 3() => { + 5 => report_nat_properties_raw_params + } +} \ No newline at end of file diff --git a/src/nex-implementation/notification/mod.rs b/src/nex-implementation/notification/mod.rs new file mode 100644 index 0000000..50cc992 --- /dev/null +++ b/src/nex-implementation/notification/mod.rs @@ -0,0 +1,159 @@ +use macros::RmcSerialize; +use rand::random; +use crate::prudp::packet::{PRUDPHeader, PRUDPPacket, PacketOption, TypesFlags}; +use crate::prudp::packet::flags::{NEED_ACK, RELIABLE}; +use crate::prudp::packet::types::DATA; +use crate::rmc::message::RMCMessage; +use crate::rmc::structures::RmcSerialize; + +#[derive(Debug, Eq, PartialEq, RmcSerialize)] +#[rmc_struct(0)] +pub struct Notification{ + pub pid_source: u32, + pub notif_type: u32, + pub param_1: u32, + pub param_2: u32, + pub str_param: String, + pub param_3: u32, +} + + +impl ConnectionData{ + pub async fn send_notification(&mut self, socket: &SocketData, notif: Notification){ + println!("sending notification"); + + let mut data = Vec::new(); + + notif.serialize(&mut data).expect("unable to write"); + + let message = RMCMessage{ + protocol_id: 14, + method_id: 1, + call_id: 1, + rest_of_data: data + }; + + println!("notif: {}", hex::encode(message.to_data())); + + + let mut prudp_packet = PRUDPPacket{ + header: PRUDPHeader{ + types_and_flags: TypesFlags::default().types(DATA).flags(NEED_ACK | RELIABLE), + source_port: socket.get_virual_port(), + destination_port: self.sock_addr.virtual_port, + ..Default::default() + }, + options: vec![ + PacketOption::FragmentId(0), + ], + payload: message.to_data(), + packet_signature: [0;16] + }; + + self.finish_and_send_packet_to(socket, prudp_packet).await; + } +} + +#[cfg(test)] +mod test{ + use std::io::Cursor; + use rand::random; + use crate::protocols::notification::Notification; + use crate::prudp::packet::{PRUDPHeader, PRUDPPacket, PacketOption, TypesFlags}; + use crate::prudp::packet::flags::{NEED_ACK, RELIABLE}; + use crate::prudp::packet::types::DATA; + use crate::rmc::message::RMCMessage; + use crate::rmc::structures::RmcSerialize; + + #[test] + fn test(){ + let data = hex::decode("ead001032900a1af62000000000000000000000000000000000000000000020100250000000e57238a6601000000001700000051b39957b90b00003661636851b3995701000001000000").unwrap(); + + + let packet = PRUDPPacket::new(&mut Cursor::new(data)).expect("invalid packet"); + + println!("{:?}", packet); + + let rmc = RMCMessage::new(&mut Cursor::new(packet.payload)).expect("invalid rmc message"); + + println!("{:?}", rmc); + + let notif = Notification::deserialize(&mut Cursor::new(rmc.rest_of_data)).expect("invalid notification"); + + println!("{:?}", notif); + } + #[test] + fn test2(){ + + let data = hex::decode("250000000e57b6801001000000001700000051b39957b90b0000248a5a9851b3995701000001000000").unwrap(); + //let packet = PRUDPPacket::new(&mut Cursor::new(data)).expect("invalid packet"); + + //println!("{:?}", packet); + + let rmc = RMCMessage::new(&mut Cursor::new(data)).expect("invalid rmc message"); + + println!("{:?}", rmc); + + let notif = Notification::deserialize(&mut Cursor::new(rmc.rest_of_data)).expect("invalid notification"); + + println!("{:?}", notif); + } + + #[test] + fn test_rmc_serialization(){ + let notif = Notification{ + pid_source: random(), + notif_type: random(), + param_1: random(), + param_2: random(), + str_param: "".to_string(), + param_3: random(), + }; + + let mut notif_data = Vec::new(); + + notif.serialize(&mut notif_data).unwrap(); + + let message = RMCMessage{ + protocol_id: 14, + method_id: 1, + call_id: random(), + rest_of_data: notif_data + }; + + let mut prudp_packet = PRUDPPacket{ + header: PRUDPHeader{ + ..Default::default() + }, + options: vec![ + PacketOption::FragmentId(0), + ], + payload: message.to_data(), + packet_signature: [0;16] + }; + + prudp_packet.set_sizes(); + + + + let mut packet_data: Vec = Vec::new(); + + prudp_packet.write_to(&mut packet_data).expect("what"); + + let packet_deserialized = PRUDPPacket::new(&mut Cursor::new(packet_data)).unwrap(); + + assert_eq!(prudp_packet, packet_deserialized); + + let message_deserialized = RMCMessage::new(&mut Cursor::new(packet_deserialized.payload)).unwrap(); + + assert_eq!(message, message_deserialized); + + let notification_deserialized = Notification::deserialize(&mut Cursor::new(message_deserialized.rest_of_data)).unwrap(); + + assert_eq!(notification_deserialized, notif); + + + + + } +} \ No newline at end of file diff --git a/src/protocols/secure/method_register.rs b/src/nex-implementation/secure/method_register.rs similarity index 97% rename from src/protocols/secure/method_register.rs rename to src/nex-implementation/secure/method_register.rs index fcfb438..0d1cd61 100644 --- a/src/protocols/secure/method_register.rs +++ b/src/nex-implementation/secure/method_register.rs @@ -2,7 +2,6 @@ use std::io::{Cursor, Write}; use std::sync::Arc; use bytemuck::bytes_of; use tokio::sync::Mutex; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::prudp::station_url::{nat_types, StationUrl}; use crate::prudp::station_url::Type::PRUDPS; use crate::prudp::station_url::UrlOptions::{Address, NatFiltering, NatMapping, NatType, Port, PrincipalID, RVConnectionID}; diff --git a/src/protocols/secure/method_send_report.rs b/src/nex-implementation/secure/method_send_report.rs similarity index 82% rename from src/protocols/secure/method_send_report.rs rename to src/nex-implementation/secure/method_send_report.rs index 81322f9..8242371 100644 --- a/src/protocols/secure/method_send_report.rs +++ b/src/nex-implementation/secure/method_send_report.rs @@ -3,11 +3,11 @@ use std::sync::Arc; use log::error; use tokio::sync::Mutex; use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::response::{RMCResponseResult}; use crate::rmc::response::ErrorCode::Core_InvalidArgument; -use crate::rmc::structures::qbuffer; +use crate::rmc::structures::{qbuffer, RmcSerialize}; +use crate::rmc::structures::qbuffer::QBuffer; pub async fn send_report(rmcmessage: &RMCMessage, report_id: u32, data: Vec) -> RMCResponseResult{ let result = tokio::fs::write(format!("./reports/{}", report_id), data).await; @@ -17,7 +17,7 @@ pub async fn send_report(rmcmessage: &RMCMessage, report_id: u32, data: Vec) Err(e) => error!("{}", e) } - return rmcmessage.success_with_data(Vec::new()); + rmcmessage.success_with_data(Vec::new()) } pub async fn send_report_raw_params(rmcmessage: &RMCMessage, _: &Arc, _conn_data: &Arc>, _: ()) -> RMCResponseResult{ @@ -27,7 +27,7 @@ pub async fn send_report_raw_params(rmcmessage: &RMCMessage, _: &Arc return rmcmessage.error_result_with_code(Core_InvalidArgument); }; - let Ok(data) = qbuffer::read(&mut reader) else { + let Ok(QBuffer(data)) = QBuffer::deserialize(&mut reader) else { return rmcmessage.error_result_with_code(Core_InvalidArgument); }; diff --git a/src/protocols/secure/mod.rs b/src/nex-implementation/secure/mod.rs similarity index 100% rename from src/protocols/secure/mod.rs rename to src/nex-implementation/secure/mod.rs diff --git a/src/protocols/server.rs b/src/nex-implementation/server.rs similarity index 79% rename from src/protocols/server.rs rename to src/nex-implementation/server.rs index 9927937..dbf93de 100644 --- a/src/protocols/server.rs +++ b/src/nex-implementation/server.rs @@ -5,10 +5,11 @@ use std::sync::Arc; use log::error; use tokio::sync::Mutex; use crate::prudp::packet::PRUDPPacket; -use crate::prudp::socket::{ConnectionData, SocketData}; use crate::rmc::message::RMCMessage; use crate::rmc::response::{RMCResponse, RMCResponseResult, send_response}; use crate::rmc::response::ErrorCode::Core_NotImplemented; +use crate::web::DirectionalData::Incoming; +use crate::web::WEB_DATA; type ContainedProtocolList = Box<[Box Fn(&'a RMCMessage, &'a Arc, &'a Arc>) -> Pin> + Send + 'a>> + Send + Sync>]>; @@ -20,6 +21,13 @@ impl RMCProtocolServer{ } pub async fn process_message(&self, packet: PRUDPPacket, socket: Arc, connection: Arc>){ + let locked = connection.lock().await; + let addr = locked.sock_addr.regular_socket_addr; + drop(locked); + let mut web = WEB_DATA.lock().await; + web.data.push((addr, Incoming(hex::encode(&packet.payload)))); + drop(web); + let Ok(rmc) = RMCMessage::new(&mut Cursor::new(&packet.payload)) else { error!("error reading rmc message"); return; @@ -29,7 +37,9 @@ impl RMCProtocolServer{ for proto in &self.0 { if let Some(response) = proto(&rmc, &socket, &connection).await { - + if matches!(response.response_result, RMCResponseResult::Error {..}){ + error!("an rmc error occurred") + } let mut locked = connection.lock().await; send_response(&packet, &socket, &mut locked, response).await; drop(locked); diff --git a/src/nex/auth_handler.rs b/src/nex/auth_handler.rs new file mode 100644 index 0000000..d0b5e2e --- /dev/null +++ b/src/nex/auth_handler.rs @@ -0,0 +1,167 @@ +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::rmc::structures::RmcSerialize; +use crate::{define_rmc_proto, kerberos, rmc}; +use macros::rmc_struct; + +define_rmc_proto!( + proto AuthClientProtocol{ + Auth + } +); + +#[rmc_struct(AuthClientProtocol)] +pub struct AuthHandler { + pub destination_server_acct: &'static Account, + pub build_name: &'static str, + pub station_url: &'static str, +} + +pub fn generate_ticket( + source_act_login_data: (u32, [u8; 16]), + dest_act_login_data: (u32, [u8; 16]), +) -> Box<[u8]> { + let source_key = derive_key(source_act_login_data.0, source_act_login_data.1); + let dest_key = derive_key(dest_act_login_data.0, dest_act_login_data.1); + + let internal_data = kerberos::TicketInternalData::new(source_act_login_data.0); + + let encrypted_inner = internal_data.encrypt(dest_key); + let encrypted_session_ticket = Ticket { + pid: dest_act_login_data.0, + session_key: internal_data.session_key, + } + .encrypt(source_key, &encrypted_inner); + + encrypted_session_ticket +} + +async fn get_login_data_by_pid(pid: u32) -> Option<(u32, [u8; 16])> { + let Ok(mut client) = account::Client::new().await else { + return None; + }; + + let Ok(passwd) = client.get_nex_password(pid).await else { + return None; + }; + + Some((pid, passwd)) +} + +impl Auth for AuthHandler { + async fn login(&self, name: String) -> Result<(), ErrorCode> { + todo!() + } + + async fn login_ex( + &self, + name: String, + extra_data: Any, + ) -> Result<(QResult, u32, Vec, ConnectionData, String), ErrorCode> { + let Ok(pid) = name.parse() else { + return Err(ErrorCode::Core_InvalidArgument); + }; + + let Ok(mut client) = account::Client::new().await else { + return Err(ErrorCode::Core_Exception); + }; + + let Ok(passwd) = client.get_nex_password(pid).await else { + return Err(ErrorCode::Core_Exception); + }; + + let source_login_data = (pid, passwd); + let destination_login_data = self.destination_server_acct.get_login_data(); + + let ticket = generate_ticket(source_login_data, destination_login_data); + + let result = QResult::success(Core_Unknown); + + let connection_data = ConnectionData { + station_url: self.station_url.to_string(), + special_station_url: "".to_string(), + //date_time: KerberosDateTime::new(1,1,1,1,1,1), + date_time: KerberosDateTime::now(), + special_protocols: Vec::new(), + }; + + Ok(( + result, + source_login_data.0, + ticket.into(), + connection_data, + self.build_name.to_owned(), + )) + } + + async fn request_ticket( + &self, + source_pid: u32, + destination_pid: u32, + ) -> Result<(QResult, Vec), ErrorCode> { + let Some(source_login_data) = get_login_data_by_pid(source_pid).await else { + return Err(ErrorCode::Core_Exception); + }; + + let desgination_login_data = if destination_pid == self.destination_server_acct.pid { + self.destination_server_acct.get_login_data() + } else { + let Some(login) = get_login_data_by_pid(destination_pid).await else { + return Err(ErrorCode::Core_Exception); + }; + login + }; + + let result = QResult::success(Core_Unknown); + + let ticket = generate_ticket(source_login_data, desgination_login_data); + + Ok((result, ticket.into())) + } + + async fn get_pid(&self, username: String) -> Result { + Err(ErrorCode::Core_Exception) + } + + async fn get_name(&self, pid: u32) -> Result { + Err(ErrorCode::Core_Exception) + } +} + +#[cfg(test)] +mod test { + use crate::rmc::structures::connection_data::ConnectionData; + use crate::rmc::structures::qresult::QResult; + use crate::rmc::structures::RmcSerialize; + use crate::rmc::response::RMCResponse; + use std::io::Cursor; + + #[test] + fn test() { + + let stuff = hex::decode("200100000a0106000000028000000100010051b3995774000000a6321c7f78847c1c5e9fb825eb26bd91841f1a40d92fc694159666119cb13527f1463ac48ad42a63e6613ede67041554b1770978112e6f1f3e177a2bfc75933216dbe38f70133a1eb28e2ae32a4b5c4b0c3e3efd4c02907992e259b257270b57a9dbe7792f4721b07f8fafb9e32d50f2555c616a015c0000004b007072756470733a2f5049443d323b7369643d313b73747265616d3d31303b747970653d323b616464726573733d322e3234332e39352e3131333b706f72743d31303030313b4349443d3100000000000100002c153ba51f00000033006272616e63683a6f726967696e2f70726f6a6563742f7775702d61676d6a206275696c643a335f385f31355f323030345f3000").unwrap(); + let stuff = RMCResponse::new(&mut Cursor::new(stuff)).unwrap(); + + let crate::rmc::response::RMCResponseResult::Success { call_id, method_id, data: stuff} = stuff.response_result else { + panic!() + }; + + + + // let stuff = hex::decode("0100010051B399577400000085F1736FCFBE93660275A3FE36FED6C2EFC57222AC99A9219CF54170A415B02DF1463AC48AD42A6307813FDE67041554B177097832ED000F892D9551A09F88E9CB0388DC1BC9527CC7384556A3287B2A349ABBF7E34A5A3EC14C2287CC7F78DA616BC3B03A035347FBD2E9A505C8EF42447CD809015F0000004E007072756470733A2F73747265616D3D31303B747970653D323B616464726573733D3139322E3136382E3137382E3132303B706F72743D31303030313B4349443D313B5049443D323B7369643D310000000000010000CDF53AA51F00000033006272616E63683A6F726967696E2F70726F6A6563742F7775702D61676D6A206275696C643A335F385F31355F323030345F3000").unwrap(); + // let stuff = hex::decode("0100010051b399577400000037d3d4814d2b16dd546c94a75d32637b45f856b5abe73cf26cfaa235c5f2c1cef1463ac48ad42a637d873fde67041554b177097880cfa7e10bb810eaf686bfb0a0cf3d65b1f476ebc046d0855327986f557dca14fbb8594883c186b863f2206f22baa0309dbcc81da2f883cb2cdc12628ec7fced015c0000004b007072756470733a2f5049443d323b7369643d313b73747265616d3d31303b747970653d323b616464726573733d322e3234332e39352e3131333b706f72743d31303030313b4349443d310000000000010000b7f33aa51f00000033006272616e63683a6f726967696e2f70726f6a6563742f7775702d61676d6a206275696c643a335f385f31355f323030345f3000").unwrap(); + + let data = <(QResult, u32, Vec, ConnectionData, String) as RmcSerialize>::deserialize( + &mut Cursor::new(stuff), + ).unwrap(); + + println!("data: {:?}", data); + } +} diff --git a/src/nex/mod.rs b/src/nex/mod.rs index 3ce1632..623d6d8 100644 --- a/src/nex/mod.rs +++ b/src/nex/mod.rs @@ -1 +1,3 @@ -pub mod account; \ No newline at end of file +pub mod account; +pub mod auth_handler; +pub mod user; \ No newline at end of file diff --git a/src/nex/user.rs b/src/nex/user.rs new file mode 100644 index 0000000..77ebfb6 --- /dev/null +++ b/src/nex/user.rs @@ -0,0 +1,45 @@ +use std::net::{Ipv4Addr, SocketAddrV4}; +use macros::rmc_struct; +use crate::define_rmc_proto; +use crate::prudp::station_url::{nat_types, StationUrl}; +use crate::prudp::station_url::Type::PRUDPS; +use crate::prudp::station_url::UrlOptions::{Address, NatFiltering, NatMapping, NatType, Port, PrincipalID, RVConnectionID}; +use crate::rmc::protocols::secure::{RemoteAuth, RawAuthInfo, RawAuth, Auth}; +use crate::rmc::response::ErrorCode; +use crate::rmc::structures::qresult::QResult; + +define_rmc_proto!( + proto UserProtocol{ + Auth + } +); + +#[rmc_struct(UserProtocol)] +pub struct User { + pub pid: u32, + pub ip: SocketAddrV4, +} + +impl Auth for User{ + async fn register(&self, station_urls: Vec) -> Result<(QResult, u32, String), ErrorCode> { + let public_station = StationUrl{ + url_type: PRUDPS, + options: vec![ + RVConnectionID(0), + Address(*self.ip.ip()), + Port(self.ip.port()), + NatFiltering(0), + NatMapping(0), + NatType(nat_types::BEHIND_NAT), + PrincipalID(self.pid), + ] + }; + + let result = QResult::success(ErrorCode::Core_Unknown); + + Ok((result, 0, public_station.to_string())) + } +} + + + diff --git a/src/protocols/matchmake_common/mod.rs b/src/protocols/matchmake_common/mod.rs deleted file mode 100644 index 79f7c97..0000000 --- a/src/protocols/matchmake_common/mod.rs +++ /dev/null @@ -1,63 +0,0 @@ -use std::collections::{BTreeMap}; -use std::sync::Arc; -use log::error; -use tokio::sync::Mutex; -use crate::protocols::notification::Notification; -use crate::prudp::socket::{ConnectionData, SocketData}; -use crate::rmc::structures::matchmake::MatchmakeSession; - -pub struct ExtendedMatchmakeSession{ - pub session: MatchmakeSession, - pub connected_players: Vec>>, -} - -pub struct MatchmakeData{ - pub(crate) matchmake_sessions: BTreeMap>> -} - -impl ExtendedMatchmakeSession{ - pub async fn add_player(&mut self, socket: &SocketData, conn: Arc>, join_msg: String) { - let Some(pid) = conn.lock().await.active_connection_data.as_ref() - .map(|c| - c.active_secure_connection_data.as_ref() - .map(|c| c.pid - ) - ).flatten() else { - error!("tried to add player without secure connection"); - return - }; - - self.connected_players.push(conn); - - - for conn in &self.connected_players{ - let Some(other_pid) = conn.lock().await.active_connection_data.as_ref() - .map(|c| - c.active_secure_connection_data.as_ref() - .map(|c| c.pid - ) - ).flatten() else { - error!("tried to send connection notification to player secure connection"); - return - }; - - let mut conn = conn.lock().await; - - conn.send_notification(socket, Notification{ - pid_source: pid, - notif_type: 3001, - param_1: self.session.gathering.self_gid, - param_2: other_pid, - str_param: join_msg.clone(), - }).await; - - - } - } -} - -impl MatchmakeData { - pub async fn try_find_session_with_criteria(&self, ) -> Option>>{ - None - } -} \ No newline at end of file diff --git a/src/protocols/notification/mod.rs b/src/protocols/notification/mod.rs deleted file mode 100644 index 7c96090..0000000 --- a/src/protocols/notification/mod.rs +++ /dev/null @@ -1,48 +0,0 @@ -use macros::RmcSerialize; -use rand::random; -use crate::prudp::packet::{PRUDPHeader, PRUDPPacket, TypesFlags}; -use crate::prudp::packet::flags::{NEED_ACK, RELIABLE}; -use crate::prudp::packet::types::DATA; -use crate::prudp::socket::{ConnectionData, SocketData}; -use crate::rmc::message::RMCMessage; -use crate::rmc::structures::RmcSerialize; - -#[derive(RmcSerialize)] -#[rmc_struct(0)] -pub struct Notification{ - pub pid_source: u32, - pub notif_type: u32, - pub param_1: u32, - pub param_2: u32, - pub str_param: String, -} - -impl ConnectionData{ - pub async fn send_notification(&mut self, socket: &SocketData, notif: Notification){ - - let mut data = Vec::new(); - - notif.serialize(&mut data).expect("unable to write"); - - let message = RMCMessage{ - protocol_id: 0xE, - method_id: 1, - call_id: random(), - rest_of_data: data - }; - - let prudp_packet = PRUDPPacket{ - header: PRUDPHeader{ - types_and_flags: TypesFlags::default().types(DATA).flags(NEED_ACK | RELIABLE), - source_port: socket.get_virual_port(), - destination_port: self.sock_addr.virtual_port, - ..Default::default() - }, - options: Vec::new(), - payload: message.to_data(), - packet_signature: [0;16] - }; - - self.finish_and_send_packet_to(socket, prudp_packet).await; - } -} \ No newline at end of file diff --git a/src/prudp/mod.rs b/src/prudp/mod.rs index b86e522..966523e 100644 --- a/src/prudp/mod.rs +++ b/src/prudp/mod.rs @@ -2,6 +2,7 @@ pub mod packet; pub mod router; pub mod socket; mod auth_module; -mod sockaddr; +pub mod sockaddr; +pub mod station_url; pub mod secure; -pub mod station_url; \ No newline at end of file +pub mod unsecure; \ No newline at end of file diff --git a/src/prudp/packet.rs b/src/prudp/packet.rs index 434d476..75dc211 100644 --- a/src/prudp/packet.rs +++ b/src/prudp/packet.rs @@ -40,7 +40,7 @@ pub enum Error { pub type Result = std::result::Result; #[repr(transparent)] -#[derive(Copy, Clone, Pod, Zeroable, SwapEndian, Default)] +#[derive(Copy, Clone, Pod, Zeroable, SwapEndian, Default, Eq, PartialEq)] pub struct TypesFlags(u16); impl TypesFlags { @@ -96,10 +96,11 @@ impl Debug for TypesFlags { } #[repr(transparent)] -#[derive(PartialEq, Eq, Copy, Clone, Pod, Zeroable, SwapEndian, Hash)] +#[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 @@ -141,7 +142,7 @@ impl Debug for VirtualPort { } #[repr(C)] -#[derive(Debug, Copy, Clone, Pod, Zeroable, SwapEndian)] +#[derive(Debug, Copy, Clone, Pod, Zeroable, SwapEndian, Eq, PartialEq)] pub struct PRUDPHeader { pub magic: [u8; 2], pub version: u8, @@ -173,7 +174,7 @@ impl Default for PRUDPHeader{ } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub enum PacketOption{ SupportedFunctions(u32), ConnectionSignature([u8; 16]), @@ -236,7 +237,7 @@ impl PacketOption{ } } -#[derive(Debug, Clone)] +#[derive(Debug, Default, Clone, Eq, PartialEq)] pub struct PRUDPPacket { pub header: PRUDPHeader, pub packet_signature: [u8; 16], @@ -290,9 +291,12 @@ impl PRUDPPacket { 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)?; @@ -372,6 +376,7 @@ impl PRUDPPacket { types_and_flags: flags, sequence_id: self.header.sequence_id, substream_id: self.header.substream_id, + session_id: self.header.session_id, ..base.header }, options, @@ -474,6 +479,8 @@ impl PRUDPPacket { #[cfg(test)] mod test { + use crate::prudp::packet::flags::{NEED_ACK, RELIABLE}; + use crate::prudp::packet::types::DATA; use super::{OptionId, PacketOption, PRUDPHeader, TypesFlags, VirtualPort}; #[test] fn size_test() { @@ -523,4 +530,13 @@ mod test { let header_data: [u8; 8] = bytes.try_into().unwrap(); } + + #[test] + 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/src/prudp/router.rs b/src/prudp/router.rs index f70d5ec..8621236 100644 --- a/src/prudp/router.rs +++ b/src/prudp/router.rs @@ -11,7 +11,7 @@ use once_cell::sync::Lazy; use log::{error, info, trace}; use thiserror::Error; use tokio::sync::RwLock; -use crate::prudp::socket::SocketData; +use crate::prudp::socket::{new_socket_pair, AnyInternalSocket, CryptoHandler, ExternalSocket}; use crate::prudp::packet::{PRUDPPacket, VirtualPort}; use crate::prudp::router::Error::VirtualPortTaken; @@ -22,10 +22,9 @@ static SERVER_DATAGRAMS: Lazy = Lazy::new(||{ }); pub struct Router { - endpoints: RwLock<[Option>; 16]>, + endpoints: RwLock<[Option>; 16]>, running: AtomicBool, socket: Arc, - //pub auth_module: Arc _no_outside_construction: PhantomData<()> } #[derive(Debug, Error)] @@ -36,9 +35,6 @@ pub enum Error{ impl Router { - fn process_prudp_packet(&self, _packet: &PRUDPPacket){ - - } async fn process_prudp_packets<'a>(self: Arc, _socket: Arc, addr: SocketAddrV4, udp_message: Vec){ let mut stream = Cursor::new(&udp_message); @@ -54,6 +50,7 @@ impl Router { trace!("got valid prudp packet from someone({}): \n{:?}", addr, packet); let connection = packet.source_sockaddr(addr); + let endpoints = self.endpoints.read().await; @@ -69,7 +66,9 @@ impl Router { trace!("sending packet to endpoint"); - endpoint.process_packet(connection, &packet).await; + tokio::spawn(async move { + endpoint.recieve_packet(connection, packet).await + }); } } @@ -88,6 +87,7 @@ impl Router { continue; }; + let current_msg = &msg_buffer[0..len]; tokio::spawn(self.clone().process_prudp_packets(socket.clone(), addr, current_msg.to_vec())); @@ -144,18 +144,22 @@ impl Router { } // returns Some(()) i - pub(crate) async fn add_socket(&self, socket: Arc) -> Result<(), Error>{ + pub(crate) async fn add_socket(&self, virtual_port: VirtualPort, encryption: E) + -> Result{ let mut endpoints = self.endpoints.write().await; - let idx = socket.get_virual_port().get_port_number() as usize; + let idx = virtual_port.get_port_number() as usize; - if endpoints[idx].is_none() { - endpoints[idx] = Some(socket); - } else { + // dont create the socket if we dont need to + if !endpoints[idx].is_none(){ return Err(VirtualPortTaken(idx as u8)); } - Ok(()) + let (internal, external) = new_socket_pair(virtual_port, encryption, self.socket.clone()); + + endpoints[idx] = Some(internal); + + Ok(external) } pub fn get_own_address(&self) -> SocketAddrV4{ diff --git a/src/prudp/secure.rs b/src/prudp/secure.rs index 916971d..486500f 100644 --- a/src/prudp/secure.rs +++ b/src/prudp/secure.rs @@ -4,10 +4,13 @@ use log::error; use rc4::cipher::StreamCipherCoreWrapper; use rc4::{KeyInit, Rc4, Rc4Core, StreamCipher}; use rc4::consts::U16; +use typenum::U5; use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; use crate::kerberos::{derive_key, TicketInternalData}; use crate::nex::account::Account; -use crate::prudp::socket::EncryptionPair; +use crate::prudp::packet::PRUDPPacket; +use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance, EncryptionPair}; +use crate::prudp::unsecure::UnsecureInstance; use crate::rmc::structures::RmcSerialize; pub fn read_secure_connection_data(data: &[u8], act: &Account) -> Option<([u8; 32], u32, u32)>{ @@ -73,12 +76,12 @@ pub fn read_secure_connection_data(data: &[u8], act: &Account) -> Option<([u8; 3 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{ - send: Box::new(Rc4U32::new_from_slice(&session_key).expect("unable to create rc4")), - recv: Box::new(Rc4U32::new_from_slice(&session_key).expect("unable to create rc4")) + send: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"), + recv: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4") }); for _ in 1..=count{ @@ -91,10 +94,99 @@ pub fn generate_secure_encryption_pairs(mut session_key: [u8; 32], count: u8) -> } vec.push(EncryptionPair{ - send: Box::new(Rc4U32::new_from_slice(&session_key).expect("unable to create rc4")), - recv: Box::new(Rc4U32::new_from_slice(&session_key).expect("unable to create rc4")) + send: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4"), + recv: Rc4U32::new_from_slice(&session_key).expect("unable to create rc4") }); } vec +} + + +pub struct Secure(pub &'static str, pub &'static Account); + + +pub struct SecureInstance { + access_key: &'static str, + session_key: [u8; 32], + streams: Vec>>, + self_signature: [u8; 16], + remote_signature: [u8; 16], + pid: u32, +} + +impl CryptoHandler for Secure { + type CryptoConnectionInstance = SecureInstance; + + fn instantiate( + &self, + remote_signature: [u8; 16], + self_signature: [u8; 16], + payload: &[u8], + substream_count: u8, + ) -> Option<(Vec, Self::CryptoConnectionInstance)> { + let (session_key, pid, check_value) = read_secure_connection_data(payload, &self.1)?; + + 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, substream_count); + + Some(( + response, + SecureInstance { + pid, + streams: encryption_pairs, + session_key, + access_key: self.0, + remote_signature, + self_signature, + }, + )) + } + + fn sign_pre_handshake(&self, packet: &mut PRUDPPacket) { + packet.set_sizes(); + packet.calculate_and_assign_signature(self.0, None, None); + } +} + + +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){ + 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){ + crypt_pair.send.apply_keystream(data); + } + } + + fn get_user_id(&self) -> u32 { + self.pid + } + + fn sign_connect(&self, packet: &mut PRUDPPacket) { + packet.set_sizes(); + packet.calculate_and_assign_signature(self.access_key, None, Some(self.self_signature)); + } + + fn sign_packet(&self, packet: &mut PRUDPPacket) { + packet.set_sizes(); + packet.calculate_and_assign_signature(self.access_key, Some(self.session_key), Some(self.self_signature)); + } + + fn verify_packet(&self, packet: &PRUDPPacket) -> bool { + true + } } \ No newline at end of file diff --git a/src/prudp/sockaddr.rs b/src/prudp/sockaddr.rs index b48b8c1..fe6a9d8 100644 --- a/src/prudp/sockaddr.rs +++ b/src/prudp/sockaddr.rs @@ -5,18 +5,28 @@ use crate::prudp::packet::VirtualPort; type Md5Hmac = Hmac; -#[derive(Eq, PartialEq, Hash, Debug, Copy, Clone)] +#[derive(Eq, PartialEq, Hash, Debug, Copy, Clone, Ord, PartialOrd)] pub struct PRUDPSockAddr{ pub regular_socket_addr: SocketAddrV4, pub virtual_port: VirtualPort } + + impl PRUDPSockAddr{ - pub fn calculate_connection_signature(&self) -> [u8; 16] { + + pub fn new(regular_socket_addr: SocketAddrV4, virtual_port: VirtualPort) -> Self{ + Self{ + regular_socket_addr, + virtual_port + } + } + + pub(super) fn calculate_connection_signature(&self) -> [u8; 16] { let mut hmac = Md5Hmac::new_from_slice(&[0; 16]).expect("fuck"); let mut data = self.regular_socket_addr.ip().octets().to_vec(); - data.extend_from_slice(&self.regular_socket_addr.port().to_be_bytes()); + //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"); diff --git a/src/prudp/socket.rs b/src/prudp/socket.rs index ecf0108..5702c51 100644 --- a/src/prudp/socket.rs +++ b/src/prudp/socket.rs @@ -1,528 +1,717 @@ -use std::collections::{HashMap, VecDeque}; -use std::future::Future; -use std::ops::Deref; -use std::pin::Pin; -use tokio::net::UdpSocket; -use std::sync::{Arc}; -use tokio::sync::{Mutex, RwLock}; -use log::{error, trace, warn}; -use rand::random; -use rc4::StreamCipher; -use crate::prudp::packet::{PacketOption, PRUDPPacket, VirtualPort}; use crate::prudp::packet::flags::{ACK, HAS_SIZE, MULTI_ACK, NEED_ACK, RELIABLE}; -use crate::prudp::packet::PacketOption::{ConnectionSignature, MaximumSubstreamId, SupportedFunctions}; use crate::prudp::packet::types::{CONNECT, DATA, DISCONNECT, PING, SYN}; +use crate::prudp::packet::PacketOption::{ConnectionSignature, FragmentId, InitialSequenceId, MaximumSubstreamId, SupportedFunctions}; +use crate::prudp::packet::{PRUDPHeader, PRUDPPacket, PacketOption, TypesFlags, VirtualPort}; use crate::prudp::router::{Error, Router}; use crate::prudp::sockaddr::PRUDPSockAddr; - - +use crate::web::DirectionalData::Outgoing; +use crate::web::WEB_DATA; +use async_trait::async_trait; +use hmac::digest::consts::U5; +use log::info; +use log::{error, trace, warn}; +use once_cell::sync::Lazy; +use rand::random; +use rc4::{Key, KeyInit, Rc4, StreamCipher}; +use rocket::http::hyper::body::HttpBody; +use std::collections::{BTreeMap, HashMap, VecDeque}; +use std::fmt::{Debug, Formatter}; +use std::future::Future; +use std::marker::PhantomData; +use std::mem; +use std::net::SocketAddrV4; +use std::ops::Deref; +use std::pin::Pin; +use std::sync::{Arc, Weak}; +use tokio::net::UdpSocket; +use tokio::sync::mpsc::{channel, Receiver, Sender}; +use tokio::sync::{Mutex, RwLock}; +use tokio_stream::Stream; +use crate::nex::account::Account; // 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 Socket { - socket_data: Arc, - router: Arc, + + +pub struct EncryptionPair { + pub send: T, + pub recv: T, } - -type OnConnectHandlerFn = Box Pin, Vec, Option)>> + Send>> + Send + Sync>; -type OnDataHandlerFn = Box, Arc>) -> Pin + Send>> + Send + Sync>; - -pub struct ActiveSecureConnectionData { - pub(crate) pid: u32, - pub(crate) session_key: [u8; 32], -} - -pub struct SocketData { - virtual_port: VirtualPort, - pub socket: Arc, - pub access_key: &'static str, - connections: RwLock>, Arc>)>>, - on_connect_handler: OnConnectHandlerFn, - on_data_handler: OnDataHandlerFn, - -} - -pub struct EncryptionPair{ - pub send: Box, - pub recv: Box -} - -pub struct ActiveConnectionData { - pub reliable_client_counter: u16, - pub reliable_server_counter: u16, - pub reliable_client_queue: VecDeque, - pub encryption_pairs: Vec, - pub server_session_id: u8, - pub connection_id: u32, - pub active_secure_connection_data: Option -} - - -pub struct ConnectionData { - pub sock_addr: PRUDPSockAddr, - pub id: u64, - pub signature: [u8; 16], - pub server_signature: [u8; 16], - pub active_connection_data: Option, -} - - - - -impl Socket { - pub async fn new( - router: Arc, - port: VirtualPort, - access_key: &'static str, - on_connection_handler: OnConnectHandlerFn, - on_data_handler: OnDataHandlerFn, - ) -> Result { - trace!("creating socket on router at {} on virtual port {:?}", router.get_own_address(), port); - - let socket_data = Arc::new( - SocketData::new_unbound(&router, port, access_key, on_connection_handler, on_data_handler) - ); - - router.add_socket(socket_data.clone()).await?; - - Ok(Self { - socket_data, - router, - }) - } -} - -impl Drop for Socket { - fn drop(&mut self) { - { - let router = self.router.clone(); - - let virtual_port = self.virtual_port; - trace!("socket dropped socket will be removed from router soon"); - // it's not that important to remove it immediately so we can delay the deletion a bit if needed - tokio::spawn(async move { - router.remove_socket(virtual_port).await; - trace!("socket removed from router successfully"); - }); +impl EncryptionPair { + pub fn init_both T>(func: F) -> Self { + Self { + recv: func(), + send: func(), } } } -impl Deref for Socket { - type Target = SocketData; +pub struct NewEncryptionPair { + pub send: E, + pub recv: E, +} + +pub struct CommonConnection { + pub user_id: u32, + pub socket_addr: PRUDPSockAddr, + pub server_port: VirtualPort, + session_id: u8, +} + +struct InternalConnection { + common: Arc, + reliable_server_counter: u16, + reliable_client_counter: u16, + // maybe add connection id(need to see if its even needed) + crypto_handler_instance: E, + data_sender: Sender>, + socket: Arc +} + +impl Deref for InternalConnection{ + type Target = CommonConnection; fn deref(&self) -> &Self::Target { - &self.socket_data + &self.common } } +impl InternalConnection{ + fn next_server_count(&mut self) -> u16{ + let prev_val = self.reliable_server_counter; + let (val, _) = self.reliable_server_counter.overflowing_add(1); + self.reliable_server_counter = val; + println!("{}", prev_val); + prev_val + } +} -impl SocketData { - fn new_unbound(router: &Router, - port: VirtualPort, - access_key: &'static str, - on_connect_handler: OnConnectHandlerFn, - on_data_handler: OnDataHandlerFn, - ) -> Self { - SocketData { - socket: router.get_udp_socket(), - virtual_port: port, - connections: Default::default(), - access_key, - on_connect_handler, - on_data_handler, - } +pub struct ExternalConnection { + sending: SendingConnection, + data_receiver: Receiver>, +} + +#[derive(Clone)] +pub struct SendingConnection{ + common: Arc, + inernal: Weak> +} + +pub struct CommonSocket { + pub virtual_port: VirtualPort, + _phantom_unconstructible: PhantomData<()>, +} + +pub(super) struct InternalSocket { + common: Arc, + socket: Arc, + crypto_handler: T, + // perf note: change the code to use RwLock here instead to avoid connections being able to block one another before the data is sent off. + internal_connections: Arc< + Mutex>>>>, + >, + connection_establishment_data_sender: Mutex>>, + connection_sender: Sender, +} + +pub struct ExternalSocket { + common: Arc, + connection_receiver: Receiver, + internal: Weak, +} + +impl ExternalSocket{ + pub async fn connect(&mut self, addr: PRUDPSockAddr) -> Option{ + let socket = self.internal.upgrade()?; + + socket.connect(addr).await; + + self.connection_receiver.recv().await } - pub fn get_virual_port(&self) -> VirtualPort { - self.virtual_port + pub async fn accept(&mut self) -> Option{ + self.connection_receiver.recv().await + } +} + +impl Deref for ExternalSocket { + type Target = CommonSocket; + fn deref(&self) -> &Self::Target { + &self.common + } +} + +impl Deref for InternalSocket { + type Target = CommonSocket; + fn deref(&self) -> &Self::Target { + &self.common + } +} + +#[async_trait] +pub(super) trait AnyInternalSocket: + Send + Sync + Deref + 'static +{ + async fn recieve_packet(&self, address: PRUDPSockAddr, packet: PRUDPPacket); + async fn connect(&self, address: PRUDPSockAddr) -> Option<()>; +} + +#[async_trait] +pub(super) trait AnyInternalConnection: + Send + Sync + Deref + 'static +{ + async fn send_data_packet(&mut self, data: Vec); +} + +#[async_trait] +impl AnyInternalConnection for InternalConnection{ + async fn send_data_packet(&mut self, data: Vec) { + let mut packet = PRUDPPacket{ + header: PRUDPHeader{ + sequence_id: self.next_server_count(), + substream_id: 0, + session_id: self.session_id, + types_and_flags: TypesFlags::default().types(DATA).flags(RELIABLE | NEED_ACK), + destination_port: self.common.socket_addr.virtual_port, + source_port: self.server_port, + ..Default::default() + }, + payload: data, + options: vec![FragmentId(0)], + ..Default::default() + }; + + self.crypto_handler_instance.encrypt_outgoing(0, &mut packet.payload[..]); + + packet.set_sizes(); + + self.crypto_handler_instance.sign_packet(&mut packet); + + let mut vec = Vec::new(); + + packet + .write_to(&mut vec) + .expect("somehow failed to convert backet to bytes"); + + println!("{}", hex::encode(&vec)); + + self.socket + .send_to(&vec, self.socket_addr.regular_socket_addr) + .await + .expect("failed to send data back"); + } +} + +impl InternalSocket { + async fn send_packet_unbuffered(&self, dest: PRUDPSockAddr, mut packet: PRUDPPacket) { + 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"); } - pub async fn process_packet(self: &Arc, client_address: PRUDPSockAddr, packet: &PRUDPPacket) { - let conn = self.connections.read().await; + async fn handle_syn(&self, address: PRUDPSockAddr, packet: PRUDPPacket) { + info!("got syn"); - if !conn.contains_key(&client_address) { - drop(conn); + let mut response = packet.base_response_packet(); - let mut conn = self.connections.write().await; - //only insert if we STILL dont have the connection preventing double insertion - if !conn.contains_key(&client_address) { - conn.insert(client_address, (Arc::new(Mutex::new(ConnectionData { - sock_addr: client_address, - id: random(), - signature: [0; 16], - server_signature: [0; 16], + response.header.types_and_flags.set_types(SYN); + response.header.types_and_flags.set_flag(ACK); + response.header.types_and_flags.set_flag(HAS_SIZE); - active_connection_data: None, - })), Arc::new(Mutex::new(())))); + let signature = address.calculate_connection_signature(); + + response.options.push(ConnectionSignature(signature)); + + // todo: refactor this to be more readable(low priority cause it doesnt change anything api wise) + for options in &packet.options { + match options { + SupportedFunctions(functions) => response + .options + .push(SupportedFunctions(*functions & 0x04)), + MaximumSubstreamId(max_substream) => response + .options + .push(MaximumSubstreamId(*max_substream)), + _ => { /* ??? */ } } - drop(conn); - } else { - drop(conn); } - let connections = self.connections.read().await; + response.set_sizes(); - let Some(conn) = connections.get(&client_address) else { - error!("connection is still not present after making sure connection is present, giving up."); + self.crypto_handler.sign_pre_handshake(&mut response); + + //println!("got syn: {:?}", response); + + self.send_packet_unbuffered(address, response) + .await; + } + + async fn connection_thread( + socket: Arc, + self_port: VirtualPort, + connection: Arc>>, + mut data_recv: Receiver> + ) { + //todo: handle stuff like resending packets if they arent acknowledged in here + while let Some(data) = data_recv.recv().await{ + let mut locked_conn = connection.lock().await; + let packet = PRUDPPacket{ + header: PRUDPHeader{ + sequence_id: locked_conn.next_server_count(), + substream_id: 0, + session_id: locked_conn.session_id, + types_and_flags: TypesFlags::default().types(DATA).flags(RELIABLE | NEED_ACK), + destination_port: locked_conn.common.socket_addr.virtual_port, + source_port: self_port, + ..Default::default() + }, + payload: data, + options: vec![FragmentId(0)], + ..Default::default() + }; + + //packet. + + + + + + + } + } + + async fn create_connection( + &self, + crypto_handler_instance: T::CryptoConnectionInstance, + socket_addr: PRUDPSockAddr, + session_id: u8, + ) { + let common = Arc::new(CommonConnection { + user_id: crypto_handler_instance.get_user_id(), + socket_addr, + session_id, + server_port: self.virtual_port + }); + + let (data_sender_from_client, data_receiver_from_client) = channel(16); + + let internal = InternalConnection { + common: common.clone(), + crypto_handler_instance, + reliable_client_counter: 2, + reliable_server_counter: 1, + data_sender: data_sender_from_client, + socket: self.socket.clone() + }; + + let internal = Arc::new(Mutex::new(internal)); + + let dyn_internal: Arc> = internal.clone(); + + let external = ExternalConnection { + sending: SendingConnection{ + common, + inernal: Arc::downgrade(&dyn_internal) + }, + data_receiver: data_receiver_from_client, + + }; + + + + + + let mut connections = self.internal_connections.lock().await; + + connections.insert(socket_addr, internal.clone()); + + drop(connections); + + self.connection_sender + .send(external) + .await + .expect("connection to external socket lost"); + } + + async fn handle_connect(&self, address: PRUDPSockAddr, packet: PRUDPPacket) { + info!("got connect"); + let Some(MaximumSubstreamId(max_substream)) = packet + .options + .iter() + .find(|v| matches!(v, MaximumSubstreamId(_))) + else { return; }; - let conn = conn.clone(); + let remote_signature = address.calculate_connection_signature(); - // dont keep holding the connections list unnescesarily + let Some(ConnectionSignature(own_signature)) = packet + .options + .iter() + .find(|p| matches!(p, ConnectionSignature(_))) + else { + error!("didnt get connection signature from client"); + return; + }; + + let session_id = packet.header.session_id; + + let Some((return_data, crypto)) = self.crypto_handler.instantiate( + remote_signature, + *own_signature, + &packet.payload, + 1 + *max_substream, + ) else { + error!("someone attempted to connect with invalid data"); + return; + }; + + let mut response = packet.base_response_packet(); + response.header.types_and_flags.set_types(CONNECT); + response.header.types_and_flags.set_flag(ACK); + response.header.types_and_flags.set_flag(HAS_SIZE); + + response.header.session_id = session_id; + response.header.sequence_id = 1; + + response.payload = return_data; + + + //let remote_signature = address.calculate_connection_signature(); + + response + .options + .push(ConnectionSignature(Default::default())); + + for option in &packet.options { + match option { + MaximumSubstreamId(max_substream) => response + .options + .push(MaximumSubstreamId(*max_substream)), + SupportedFunctions(funcs) => { + response.options.push(SupportedFunctions(*funcs)) + } + _ => { /* ? */ } + } + } + + + response.set_sizes(); + + crypto.sign_connect(&mut response); + + //println!("connect out: {:?}", response); + + self.create_connection(crypto, address, session_id).await; + + self.send_packet_unbuffered(address, response).await; + } + + async fn handle_data(&self, address: PRUDPSockAddr, mut packet: PRUDPPacket) { + info!("got data"); + + if packet.header.types_and_flags.get_flags() & (NEED_ACK | RELIABLE) != (NEED_ACK | RELIABLE){ + error!("invalid or unimplemented packet flags"); + } + + let connections = self.internal_connections.lock().await; + let Some(conn) = connections.get(&address) else{ + error!("tried to send data on inactive connection!"); + return + }; + let conn = conn.clone(); drop(connections); - let mut connection = conn.0.lock().await; - //let _mutual_exclusion_packet_handeling_mtx = conn.1.lock().await; + let mut conn = conn.lock().await; + conn.crypto_handler_instance.decrypt_incoming(packet.header.substream_id, &mut packet.payload[..]); + + let mut data = Vec::new(); + + mem::swap(&mut data, &mut packet.payload); + + let mut response = packet.base_acknowledgement_packet(); + response.header.types_and_flags.set_flag(HAS_SIZE | ACK); + response.header.session_id = conn.session_id; + + conn.crypto_handler_instance.sign_packet(&mut response); + + self.send_packet_unbuffered(address, response).await; + + conn.data_sender.send(data).await.ok(); + + + } + + async fn handle_ping(&self, address: PRUDPSockAddr, packet: PRUDPPacket){ + let connections = self.internal_connections.lock().await; + let Some(conn) = connections.get(&address) else{ + error!("tried to send data on inactive connection!"); + return + }; + let conn = conn.clone(); + drop(connections); + + let mut conn = conn.lock().await; + + let mut response = packet.base_acknowledgement_packet(); + response.header.types_and_flags.set_flag(HAS_SIZE | ACK); + response.header.session_id = conn.session_id; + + conn.crypto_handler_instance.sign_packet(&mut response); + + self.send_packet_unbuffered(address, response).await; + } + + async fn handle_disconnect(&self, address: PRUDPSockAddr, packet: PRUDPPacket){ + let connections = self.internal_connections.lock().await; + let Some(conn) = connections.get(&address) else{ + error!("tried to send data on inactive connection!"); + return + }; + let conn = conn.clone(); + drop(connections); + + let mut conn = conn.lock().await; + + let mut response = packet.base_acknowledgement_packet(); + response.header.types_and_flags.set_flag(HAS_SIZE | ACK); + response.header.session_id = conn.session_id; + + 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; + } +} + +#[async_trait] +impl AnyInternalSocket for InternalSocket { + async fn recieve_packet(&self, address: PRUDPSockAddr, packet: PRUDPPacket) { + // todo: handle acks if (packet.header.types_and_flags.get_flags() & ACK) != 0 { - //todo: handle acknowledgements and resending packets propperly - println!("got ack"); + info!("got ack"); + if packet.header.types_and_flags.get_types() == SYN || + packet.header.types_and_flags.get_types() == CONNECT{ + + if packet.header.types_and_flags.get_types() == SYN{ + println!("Syn: {:?}", packet); + } + + if packet.header.types_and_flags.get_types() == CONNECT{ + println!("Connect: {:?}", packet); + } + + let sender = self.connection_establishment_data_sender.lock().await; + info!("redirecting ack to active connection establishment code"); + + if let Some(conn) = sender.as_ref(){ + if let Err(e) = conn.send(packet).await { + error!("error whilest sending data to connection establishment: {}", e); + } + } else { + error!("got connection response without the active reciever being present"); + } + } return; } if (packet.header.types_and_flags.get_flags() & MULTI_ACK) != 0 { - println!("got ack"); + info!("got multi ack"); return; } - match packet.header.types_and_flags.get_types() { - SYN => { - println!("got syn"); - // reset heartbeat? - let mut response_packet = packet.base_response_packet(); - - response_packet.header.types_and_flags.set_types(SYN); - response_packet.header.types_and_flags.set_flag(ACK); - response_packet.header.types_and_flags.set_flag(HAS_SIZE); - - connection.signature = client_address.calculate_connection_signature(); - - response_packet.options.push(ConnectionSignature(connection.signature)); - - for options in &packet.options { - match options { - SupportedFunctions(functions) => { - response_packet.options.push(SupportedFunctions(*functions & 0x04)) - } - MaximumSubstreamId(max_substream) => { - response_packet.options.push(MaximumSubstreamId(*max_substream)) - } - _ => { /* ??? */ } - } - } - - response_packet.set_sizes(); - - response_packet.calculate_and_assign_signature(self.access_key, None, None); - - let mut vec = Vec::new(); - - response_packet.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); - - self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back"); + SYN => self.handle_syn(address, packet).await, + CONNECT => self.handle_connect(address, packet).await, + DATA => self.handle_data(address, packet).await, + DISCONNECT => self.handle_disconnect(address, packet).await, + PING => self.handle_ping(address, packet).await, + _ => { + error!( + "unimplemented packet type: {}", + packet.header.types_and_flags.get_types() + ) } - CONNECT => { - println!("got connect"); - let Some(MaximumSubstreamId(max_substream)) = packet.options.iter().find(|v| matches!(v, MaximumSubstreamId(_))) else { - return; - }; - - let Some(( - response_data, - encryption_pairs, - active_secure_connection_data - )) = (self.on_connect_handler)(packet.clone(), *max_substream).await else { - error!("invalid connection request"); - return; - }; - - - connection.active_connection_data = Some(ActiveConnectionData { - encryption_pairs, - reliable_client_queue: VecDeque::new(), - reliable_client_counter: 2, - reliable_server_counter: 1, - server_session_id: packet.header.session_id, - active_secure_connection_data, - connection_id: random() - }); - - let mut response_packet = packet.base_response_packet(); - - response_packet.payload = response_data; - - response_packet.header.types_and_flags.set_types(CONNECT); - response_packet.header.types_and_flags.set_flag(ACK); - response_packet.header.types_and_flags.set_flag(HAS_SIZE); - - // todo: (or not) sliding windows and stuff - - response_packet.header.session_id = packet.header.session_id; - response_packet.header.sequence_id = 1; - - response_packet.options.push(ConnectionSignature(Default::default())); - - //let mut init_seq_id = 0; - - for option in &packet.options { - match option { - MaximumSubstreamId(max_substream) => response_packet.options.push(MaximumSubstreamId(*max_substream)), - SupportedFunctions(funcs) => response_packet.options.push(SupportedFunctions(*funcs)), - ConnectionSignature(sig) => { - connection.server_signature = *sig - } - PacketOption::InitialSequenceId(_id) => { - //init_seq_id = *id; - } - _ => { /* ? */ } - } - } - - // Splatoon doesnt use compression so we arent gonna compress unless i at some point - // want to implement some server which requires it - // No encryption here for the same reason - - // todo: implement something to do secure servers - - if connection.server_signature == <[u8; 16] as Default>::default() { - error!("didn't get connection signature from client") - } - - response_packet.set_sizes(); - - response_packet.calculate_and_assign_signature(self.access_key, None, Some(connection.server_signature)); - - let mut vec = Vec::new(); - response_packet.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); - - self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back"); - - - } - DATA => { - if (packet.header.types_and_flags.get_flags() & RELIABLE) != 0 { - let Some(active_connection) = connection.active_connection_data.as_mut() else { - error!("got data packet on non active connection!"); - return; - }; - - match active_connection.reliable_client_queue.binary_search_by_key(&packet.header.sequence_id, |p| p.header.sequence_id) { - Ok(_) => warn!("recieved packet twice"), - Err(position) => active_connection.reliable_client_queue.insert(position, packet.clone()), - } - - - if (packet.header.types_and_flags.get_flags() & NEED_ACK) != 0 { - let mut ack = packet.base_acknowledgement_packet(); - ack.header.session_id = active_connection.server_session_id; - - ack.set_sizes(); - let potential_session_key = connection - .active_connection_data - .as_ref() - .unwrap().active_secure_connection_data - .as_ref() - .map(|s| s.session_key); - - ack.calculate_and_assign_signature(self.access_key, potential_session_key, Some(connection.server_signature)); - - let mut vec = Vec::new(); - ack.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); - - self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back"); - } - drop(connection); - while let Some(mut packet) = { - let mut locked = conn.0.lock().await; - - let packet = locked.active_connection_data.as_mut().map(|a| - a.reliable_client_queue - .front() - .is_some_and(|v| v.header.sequence_id == a.reliable_client_counter) - .then(|| a.reliable_client_queue.pop_front())).flatten().flatten(); - - drop(locked); - packet - } { - if packet.options.iter().any(|v| match v{ - PacketOption::FragmentId(f) => *f != 0, - _ => false, - }){ - error!("fragmented packets are unsupported right now") - } - - let mut locked = conn.0.lock().await; - - let active_connection = locked.active_connection_data.as_mut() - .expect("we litterally just recieved a packet which requires the connection to be active, failing this should be impossible"); - - active_connection.reliable_client_counter = active_connection.reliable_client_counter.overflowing_add(1).0; - - let Some(stream) = active_connection.encryption_pairs.get_mut(packet.header.substream_id as usize).map(|e| &mut e.recv) else { - return; - }; - - stream.apply_keystream(&mut packet.payload); - - drop(locked); - // we cant divert this off to another thread we HAVE to process it now to keep order - - (self.on_data_handler)(packet, self.clone(), conn.0.clone()).await; - // ignored for now - } - } else { - error!("unreliable packets are unimplemented"); - unimplemented!() - } - //info!("{:?}", packet); - } - PING => { - let ConnectionData { - active_connection_data, - server_signature, - .. - } = &mut *connection; - - if (packet.header.types_and_flags.get_flags() & NEED_ACK) != 0 { - let Some(active_connection) = active_connection_data.as_mut() else { - error!("got data packet on non active connection!"); - return; - }; - - let mut ack = packet.base_acknowledgement_packet(); - ack.header.session_id = active_connection.server_session_id; - - ack.set_sizes(); - - let potential_session_key = active_connection. - active_secure_connection_data - .as_ref() - .map(|s| s.session_key); - - ack.calculate_and_assign_signature(self.access_key, potential_session_key, Some(*server_signature)); - - let mut vec = Vec::new(); - ack.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); - - self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back"); - } - } - DISCONNECT => { - println!("got disconnect"); - let Some(active_connection) = &connection.active_connection_data else { - return; - }; - - let mut ack = packet.base_acknowledgement_packet(); - - ack.header.session_id = active_connection.server_session_id; - - ack.set_sizes(); - - let potential_session_key = active_connection.active_secure_connection_data - .as_ref() - .map(|s| s.session_key); - - ack.calculate_and_assign_signature(self.access_key, potential_session_key, Some(connection.server_signature)); - - let mut vec = Vec::new(); - ack.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); - - self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back"); - self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back"); - self.socket.send_to(&vec, client_address.regular_socket_addr).await.expect("failed to send data back"); - } - _ => error!("unimplemented packet type: {}", packet.header.types_and_flags.get_types()) } } + + async fn connect(&self, address: PRUDPSockAddr) -> Option<()> { + let (send, mut recv) = channel(10); + + let mut sender = self.connection_establishment_data_sender.lock().await; + *sender = Some(send); + drop(sender); + + let remote_signature = address.calculate_connection_signature(); + + let packet = PRUDPPacket{ + header: PRUDPHeader{ + source_port: self.virtual_port, + destination_port: address.virtual_port, + types_and_flags: TypesFlags::default().types(SYN).flags(NEED_ACK), + ..Default::default() + }, + options: vec![ + SupportedFunctions(0x104), + MaximumSubstreamId(0), + ConnectionSignature(remote_signature) + ], + ..Default::default() + }; + + + + self.send_packet_unbuffered(address, packet).await; + + let Some(syn_ack_packet) = recv.recv().await else{ + error!("what"); + return None; + }; + + let Some(ConnectionSignature(own_signature)) = syn_ack_packet + .options + .iter() + .find(|p| matches!(p, ConnectionSignature(_))) + else { + error!("didnt get connection signature from remote partner"); + return None; + }; + + + + let packet = PRUDPPacket{ + header: PRUDPHeader{ + source_port: self.virtual_port, + destination_port: address.virtual_port, + types_and_flags: TypesFlags::default().types(CONNECT).flags(NEED_ACK), + ..Default::default() + }, + options: vec![ + SupportedFunctions(0x04), + MaximumSubstreamId(0), + ConnectionSignature(remote_signature) + ], + ..Default::default() + }; + + self.send_packet_unbuffered(address, packet).await; + + let Some(connect_ack_packet) = recv.recv().await else{ + error!("what"); + return None; + }; + + let (_, crypt) = self.crypto_handler.instantiate(remote_signature, *own_signature, &[], 1)?; + + //todo: make this work for secure servers as well + self.create_connection(crypt, address, 0).await; + + Some(()) + } } -impl ConnectionData{ - pub async fn finish_and_send_packet_to(&mut self, socket: &SocketData, mut packet: PRUDPPacket){ - if (packet.header.types_and_flags.get_flags() & RELIABLE) != 0{ - let Some(active_connection) = self.active_connection_data.as_mut() else { - error!("tried to send a secure packet to an inactive connection"); - return; - }; +pub(super) fn new_socket_pair( + virtual_port: VirtualPort, + encryption: T, + socket: Arc, +) -> (Arc>, ExternalSocket) { + let common = Arc::new(CommonSocket { + virtual_port, + _phantom_unconstructible: Default::default(), + }); - packet.header.sequence_id = active_connection.reliable_server_counter; - active_connection.reliable_server_counter += 1; + let (connection_send, connection_recv) = channel(16); - let Some(encryption) = active_connection.encryption_pairs.get_mut(packet.header.substream_id as usize).map(|e| &mut e.send) else { - return; - }; + let internal = Arc::new(InternalSocket { + common: common.clone(), + connection_sender: connection_send, + crypto_handler: encryption, + internal_connections: Default::default(), + connection_establishment_data_sender: Default::default(), + socket, + }); - encryption.apply_keystream(&mut packet.payload); - } + let dyn_internal: Arc = internal.clone(); - packet.header.source_port = socket.virtual_port; - packet.header.destination_port = self.sock_addr.virtual_port; + let external = ExternalSocket { + common, + connection_receiver: connection_recv, + internal: Arc::downgrade(&dyn_internal), + }; - packet.set_sizes(); - - let potential_session_key = self.active_connection_data - .as_ref() - .unwrap().active_secure_connection_data - .as_ref() - .map(|s| s.session_key); - - - packet.calculate_and_assign_signature(socket.access_key, potential_session_key, Some(self.server_signature)); - - let mut vec = Vec::new(); - - packet.write_to(&mut vec).expect("somehow failed to convert backet to bytes"); - - if let Err(e) = socket.socket.send_to(&vec, self.sock_addr.regular_socket_addr).await{ - error!("unable to send packet to destination: {}", e); - } - } - - + (internal, external) } -#[cfg(test)] -mod test { - use std::io::Cursor; - use std::net::{Ipv4Addr, SocketAddrV4}; - use std::sync::Arc; - use tokio::net::UdpSocket; - use tokio::sync::mpsc::channel; - use crate::prudp::packet::{PRUDPPacket, VirtualPort}; - use crate::prudp::sockaddr::PRUDPSockAddr; - use crate::prudp::socket::SocketData; +pub trait CryptoHandlerConnectionInstance: Send + Sync + 'static { + type Encryption: StreamCipher + Send; - /*#[tokio::test] - async fn test_connect() { - let packet_1 = [234, 208, 1, 27, 0, 0, 175, 161, 192, 0, 0, 0, 0, 0, 36, 21, 233, 179, 203, 154, 57, 222, 219, 9, 21, 2, 29, 172, 56, 92, 0, 4, 4, 1, 0, 0, 1, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 0]; - let packet_2 = [234, 208, 1, 31, 0, 0, 175, 161, 225, 0, 249, 0, 1, 0, 40, 168, 31, 138, 58, 193, 30, 134, 3, 232, 205, 245, 28, 155, 193, 198, 0, 4, 0, 0, 0, 0, 1, 16, 211, 240, 113, 188, 227, 114, 114, 30, 157, 179, 246, 55, 233, 240, 44, 197, 3, 2, 247, 244, 4, 1, 0]; + fn decrypt_incoming(&mut self, substream: u8, data: &mut [u8]); + fn encrypt_outgoing(&mut self, substream: u8, data: &mut [u8]); - let packet_1 = PRUDPPacket::new(&mut Cursor::new(packet_1)).unwrap(); - let packet_2 = PRUDPPacket::new(&mut Cursor::new(packet_2)).unwrap(); + fn get_user_id(&self) -> u32; + fn sign_connect(&self, packet: &mut PRUDPPacket); + fn sign_packet(&self, packet: &mut PRUDPPacket); + fn verify_packet(&self, packet: &PRUDPPacket) -> bool; +} +pub trait CryptoHandler: Send + Sync + 'static { + type CryptoConnectionInstance: CryptoHandlerConnectionInstance; - let (send, recv) = channel(100); + fn instantiate( + &self, + remote_signature: [u8; 16], + own_signature: [u8; 16], + _: &[u8], + substream_count: u8, + ) -> Option<(Vec, Self::CryptoConnectionInstance)>; - let sock = Arc::new(SocketData { - connections: Default::default(), - access_key: "6f599f81", - virtual_port: VirtualPort(0), - socket: Arc::new(UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 10000)).await.unwrap()), - connection_creation_sender: send, - }); - println!("sent: {:?}", packet_1); - sock.process_packet(PRUDPSockAddr { - virtual_port: VirtualPort(0), - regular_socket_addr: SocketAddrV4::new(Ipv4Addr::LOCALHOST, 2469), - }, &packet_1).await; - println!("sent: {:?}", packet_2); - sock.process_packet(PRUDPSockAddr { - virtual_port: VirtualPort(0), - regular_socket_addr: SocketAddrV4::new(Ipv4Addr::LOCALHOST, 2469), - }, &packet_2).await; - }*/ + fn sign_pre_handshake(&self, packet: &mut PRUDPPacket); +} + +impl Deref for ExternalConnection{ + type Target = SendingConnection; + fn deref(&self) -> &Self::Target { + &self.sending + } +} + +impl Deref for SendingConnection{ + type Target = CommonConnection; + fn deref(&self) -> &Self::Target { + &self.common + } +} + +impl ExternalConnection{ + pub async fn recv(&mut self) -> Option>{ + self.data_receiver.recv().await + } + //todo: make this an actual result instead of an option + + pub fn duplicate_sender(&self) -> SendingConnection{ + self.sending.clone() + } +} + +impl SendingConnection{ + pub async fn send(&self, data: Vec) -> Option<()> { + println!("{}", hex::encode(&data)); + let internal = self.inernal.upgrade()?; + + let mut internal = internal.lock().await; + + internal.send_data_packet(data).await; + Some(()) + } } \ No newline at end of file diff --git a/src/prudp/unsecure.rs b/src/prudp/unsecure.rs new file mode 100644 index 0000000..12e15ca --- /dev/null +++ b/src/prudp/unsecure.rs @@ -0,0 +1,84 @@ +use once_cell::sync::Lazy; +use rc4::{Key, KeyInit, Rc4, StreamCipher}; +use typenum::U5; +use crate::prudp::packet::PRUDPPacket; +use crate::prudp::socket::{CryptoHandler, CryptoHandlerConnectionInstance, EncryptionPair}; + +pub struct Unsecure(pub &'static str); + + + +pub struct UnsecureInstance { + key: &'static str, + streams: Vec>>, + self_signature: [u8; 16], + remote_signature: [u8; 16], +} + +// 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; + + fn instantiate( + &self, + remote_signature: [u8; 16], + self_signature: [u8; 16], + _: &[u8], + substream_count: u8, + ) -> Option<(Vec, Self::CryptoConnectionInstance)> { + Some(( + Vec::new(), + UnsecureInstance { + streams: (0..substream_count) + .map(|_| EncryptionPair::init_both(|| Rc4::new(&DEFAULT_KEY))) + .collect(), + key: self.0, + remote_signature, + self_signature, + }, + )) + } + + fn sign_pre_handshake(&self, packet: &mut PRUDPPacket) { + packet.set_sizes(); + packet.calculate_and_assign_signature(self.0, None, None); + } +} + +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){ + 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){ + crypt_pair.send.apply_keystream(data); + } + } + + fn get_user_id(&self) -> u32 { + 0 + } + + fn sign_connect(&self, packet: &mut PRUDPPacket) { + packet.set_sizes(); + packet.calculate_and_assign_signature(self.key, None, Some(self.self_signature)); + } + + fn sign_packet(&self, packet: &mut PRUDPPacket) { + packet.set_sizes(); + packet.calculate_and_assign_signature(self.key, None, Some(self.self_signature)); + } + + fn verify_packet(&self, packet: &PRUDPPacket) -> bool { + true + } +} \ No newline at end of file diff --git a/src/result.rs b/src/result.rs new file mode 100644 index 0000000..31a37d1 --- /dev/null +++ b/src/result.rs @@ -0,0 +1,23 @@ +use std::error::Error; +use log::error; + +pub trait ResultExtension{ + type Output; + + fn display_err_or_some(self) -> Option; +} + +impl ResultExtension for Result{ + type Output = T; + + fn display_err_or_some(self) -> Option { + match self{ + Ok(v) => Some(v), + Err(e) => { + error!("{}", e); + + None + } + } + } +} \ No newline at end of file diff --git a/src/rmc/message.rs b/src/rmc/message.rs index 68598d2..f45b585 100644 --- a/src/rmc/message.rs +++ b/src/rmc/message.rs @@ -5,7 +5,7 @@ use log::error; use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; use crate::rmc::response::{ErrorCode, RMCResponseResult}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct RMCMessage{ pub protocol_id: u16, pub call_id: u32, @@ -60,13 +60,15 @@ impl RMCMessage{ output.write_all(bytes_of(&size)).expect("unable to write size"); - let proto_id = self.protocol_id as u8; + let proto_id = self.protocol_id as u8 | 0x80; output.write_all(bytes_of(&proto_id)).expect("unable to write size"); output.write_all(bytes_of(&self.call_id)).expect("unable to write size"); output.write_all(bytes_of(&self.method_id)).expect("unable to write size"); + output.write_all(&self.rest_of_data).expect("unable to write data"); + output } diff --git a/src/rmc/mod.rs b/src/rmc/mod.rs index bf1eb0c..23458f6 100644 --- a/src/rmc/mod.rs +++ b/src/rmc/mod.rs @@ -1,6 +1,7 @@ pub mod message; pub mod structures; pub mod response; +pub mod protocols; diff --git a/src/rmc/protocols/auth.rs b/src/rmc/protocols/auth.rs new file mode 100644 index 0000000..ed8d1cd --- /dev/null +++ b/src/rmc/protocols/auth.rs @@ -0,0 +1,47 @@ +use crate::rmc::response::ErrorCode; +use crate::rmc::structures::any::Any; +use crate::rmc::structures::connection_data::ConnectionData; +use crate::rmc::structures::qresult::QResult; +use macros::{method_id, rmc_proto}; + + +/// 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>; + + /// representation of the `LoginEx` method(for details see the + /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) + #[method_id(2)] + async fn login_ex( + &self, + name: String, + extra_data: Any, + ) -> Result<(QResult, u32, Vec, ConnectionData, String), ErrorCode>; + + /// representation of the `RequestTicket` method(for details see the + /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) + #[method_id(3)] + async fn request_ticket( + &self, + source_pid: u32, + destination_pid: u32, + ) -> Result<(QResult, Vec), ErrorCode>; + + /// representation of the `GetPID` method(for details see the + /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) + #[method_id(4)] + async fn get_pid(&self, username: String) -> Result; + + /// representation of the `LoginWithContext` method(for details see the + /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) + #[method_id(5)] + async fn get_name(&self, pid: u32) -> Result; + + // `LoginWithContext` is left out here because we don't need it right now and versioning still + // needs to be figured out +} diff --git a/src/rmc/protocols/mod.rs b/src/rmc/protocols/mod.rs new file mode 100644 index 0000000..fd09e63 --- /dev/null +++ b/src/rmc/protocols/mod.rs @@ -0,0 +1,303 @@ +#![allow(async_fn_in_trait)] + +pub mod auth; +pub mod secure; + +use crate::prudp::socket::{ExternalConnection, SendingConnection}; +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::connection_data::ConnectionData; +use crate::rmc::structures::matchmake::AutoMatchmakeParam; +use crate::rmc::structures::{Error, RmcSerialize}; +use async_trait::async_trait; +use chrono::TimeDelta; +use log::{error, info}; +use macros::method_id; +use macros::{rmc_proto, rmc_struct}; +use paste::paste; +use std::collections::HashMap; +use std::io::Cursor; +use std::ops::{Add, Deref}; +use std::sync::{Arc, Condvar}; +use std::time::Duration; +use thiserror::Error; +use tokio::sync::{Mutex, Notify}; +use tokio::time::{sleep_until, Instant}; +use crate::result::ResultExtension; + +#[derive(Error, Debug)] +pub enum RemoteCallError { + #[error("Call to remote timed out whilest waiting on response.")] + Timeout, + #[error("A server side rmc error occurred: {0:?}")] + ServerError(ErrorCode), + #[error("Connection broke")] + ConnectionBroke, + #[error("Error reading response data: {0}")] + InvalidResponse(#[from] structures::Error), +} + +pub struct RmcConnection(pub SendingConnection, pub RmcResponseReceiver); + +pub struct RmcResponseReceiver(Arc, Arc>>); + +impl RmcConnection { + pub async fn make_raw_call( + &self, + message: &RMCMessage, + ) -> Result { + self.make_raw_call_no_response(message).await?; + + let data = self.1.get_response_data(message.call_id).await?; + + let out = ::deserialize(&mut Cursor::new(data))?; + + Ok(out) + } + + pub async fn make_raw_call_no_response( + &self, + message: &RMCMessage, + ) -> Result<(), RemoteCallError> { + let message_data = message.to_data(); + + self.0.send(message_data).await.ok_or(ConnectionBroke)?; + + Ok(()) + } +} + +impl RmcResponseReceiver { + // returns none if timed out + pub async fn get_response_data(&self, call_id: u32) -> Result, RemoteCallError> { + let mut end_wait_time = Instant::now(); + end_wait_time += Duration::from_secs(5); + + let sleep_fut = sleep_until(end_wait_time); + tokio::pin!(sleep_fut); + + let mut sleep_manual_unlock_fut = Instant::now(); + sleep_manual_unlock_fut += Duration::from_secs(4); + + let sleep_manual_unlock_fut = sleep_until(sleep_manual_unlock_fut); + tokio::pin!(sleep_manual_unlock_fut); + + loop { + 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)) + } + } + + drop(locked); + + let notif_fut = self.0.notified(); + + tokio::select! { + _ = &mut sleep_manual_unlock_fut => { + continue; + } + _ = &mut sleep_fut => { + return Err(RemoteCallError::Timeout); + } + _ = notif_fut => { + continue; + } + } + } + } +} + +pub trait HasRmcConnection { + fn get_connection(&self) -> &RmcConnection; +} + +pub trait RemoteObject { + fn new(conn: RmcConnection) -> Self; +} + +impl RemoteObject for () { + fn new(_: RmcConnection) -> Self {} +} + +pub trait RmcCallable { + //type Remote: RemoteObject; + fn rmc_call( + &self, + responder: &SendingConnection, + protocol_id: u16, + method_id: u32, + call_id: u32, + rest: Vec, + ) -> impl std::future::Future + Send; +} + +#[macro_export] +macro_rules! define_rmc_proto { + (proto $name:ident{ + $($protocol:path),* + }) => { + paste::paste!{ + pub trait []: std::any::Any $( + [] + $protocol)* { + async fn rmc_call(&self, remote_response_connection: &crate::prudp::socket::SendingConnection, 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, + )* + v => log::error!("invalid protocol called on rmc object {}", v) + } + } + } + + pub struct [](crate::rmc::protocols::RmcConnection); + + impl crate::rmc::protocols::RemoteInstantiatable for []{ + fn new(conn: crate::rmc::protocols::RmcConnection) -> Self{ + Self(conn) + } + } + + impl crate::rmc::protocols::HasRmcConnection for []{ + fn get_connection(&self) -> &crate::rmc::protocols::RmcConnection{ + &self.0 + } + } + + $( + impl [] for []{} + )* + } + }; +} + +/// This is a special case to allow unit to represent the fact that no object is represented. +impl RmcCallable for () { + async fn rmc_call( + &self, + remote_response_connection: &crate::prudp::socket::SendingConnection, + protocol_id: u16, + method_id: u32, + call_id: u32, + rest: Vec, + ) { + //todo: maybe reply with not implemented(?) + } +} + +pub trait RemoteInstantiatable{ + fn new(conn: RmcConnection) -> Self; +} + +pub struct OnlyRemote(T); + +impl Deref for OnlyRemote{ + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl OnlyRemote{ + pub fn new(conn: RmcConnection) -> Self{ + Self(T::new(conn)) + } +} + +impl RmcCallable for OnlyRemote{ + fn rmc_call(&self, responder: &SendingConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec) -> impl std::future::Future + Send { + async{} + } +} + +async fn handle_incoming( + mut connection: ExternalConnection, + remote: Arc, + notify: Arc, + incoming: Arc>>, +) { + let sending_conn = connection.duplicate_sender(); + + 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 + }; + + if (proto_id & 0x80) == 0{ + let Some(response) = RMCResponse::new(&mut Cursor::new(v)).display_err_or_some() else { + error!("ending rmc gateway."); + return + }; + + info!("got rmc response"); + + let mut locked = incoming.lock().await; + + locked.insert(response.get_call_id(), response); + notify.notify_waiters(); + } else { + let Some(message) = RMCMessage::new(&mut Cursor::new(v)).display_err_or_some() else { + error!("ending rmc gateway."); + return + }; + + let RMCMessage{ + protocol_id, + method_id, + call_id, + rest_of_data + } = message; + + info!("got rmc request, handeling it now..."); + + remote.rmc_call(&sending_conn, protocol_id, method_id, call_id, rest_of_data).await; + + + } + } +} + +pub fn new_rmc_gateway_connection(conn: ExternalConnection, create_internal: F) -> Arc +where + F: FnOnce(RmcConnection) -> T, +{ + let notify = Arc::new(Notify::new()); + let incoming: Arc>> = Default::default(); + + let response_recv = RmcResponseReceiver(notify.clone(), incoming.clone()); + + let sending_conn = conn.duplicate_sender(); + + let rmc_conn = RmcConnection(sending_conn, response_recv); + + let exposed_object = (create_internal)(rmc_conn); + + let exposed_object = Arc::new(exposed_object); + + { + let exposed_object = exposed_object.clone(); + tokio::spawn(async move { + handle_incoming( + conn, + exposed_object, + notify, + incoming + ).await; + }); + } + + exposed_object +} diff --git a/src/rmc/protocols/secure.rs b/src/rmc/protocols/secure.rs new file mode 100644 index 0000000..fa11c35 --- /dev/null +++ b/src/rmc/protocols/secure.rs @@ -0,0 +1,12 @@ +use macros::{method_id, rmc_proto}; +use crate::prudp::station_url::StationUrl; +use crate::rmc::response::ErrorCode; +use crate::rmc::structures::any::Any; +use crate::rmc::structures::connection_data::ConnectionData; +use crate::rmc::structures::qresult::QResult; + +#[rmc_proto(11)] +pub trait Auth { + #[method_id(1)] + async fn register(&self, station_urls: Vec) -> Result<(QResult, u32, String), ErrorCode>; +} diff --git a/src/rmc/response.rs b/src/rmc/response.rs index 7d16ba4..f47ee66 100644 --- a/src/rmc/response.rs +++ b/src/rmc/response.rs @@ -1,44 +1,114 @@ use std::io; -use std::io::{Write}; +use std::io::{Read, Seek, Write}; use std::mem::transmute; use bytemuck::bytes_of; +use log::error; +use v_byte_macros::EnumTryInto; +use crate::endianness::{ReadExtensions, IS_BIG_ENDIAN}; use crate::prudp::packet::{PRUDPPacket}; use crate::prudp::packet::flags::{NEED_ACK, RELIABLE}; use crate::prudp::packet::PacketOption::FragmentId; use crate::prudp::packet::types::DATA; -use crate::prudp::socket::{ConnectionData, SocketData}; +use crate::prudp::socket::{ExternalConnection, SendingConnection}; +use crate::rmc::response::ErrorCode::Core_Exception; use crate::rmc::structures::qresult::ERROR_MASK; +use crate::rmc::structures::RmcSerialize; +use crate::web::DirectionalData::{Incoming, Outgoing}; +use crate::web::WEB_DATA; pub enum RMCResponseResult { - Success{ + Success { call_id: u32, method_id: u32, data: Vec, }, - Error{ + Error { error_code: ErrorCode, call_id: u32, - } + }, } pub struct RMCResponse { pub protocol_id: u8, - pub response_result: RMCResponseResult + pub response_result: RMCResponseResult, } impl RMCResponse { - pub fn to_data(self) -> Vec{ + pub fn new(stream: &mut (impl Seek + Read)) -> io::Result{ + // ignore the size for now this will only be used for checking + let size: u32 = stream.read_struct(IS_BIG_ENDIAN)?; + + let protocol_id: u8 = stream.read_struct(IS_BIG_ENDIAN)?; + + /*let protocol_id: u16 = match protocol_id{ + 0x7F => { + stream.read_struct(IS_BIG_ENDIAN)? + }, + _ => protocol_id as u16 + };*/ + + let is_success: u8 = stream.read_struct(IS_BIG_ENDIAN)?; + + let response_result = if is_success == 0x01{ + let call_id: u32 = stream.read_struct(IS_BIG_ENDIAN)?; + let method_id: u32 = stream.read_struct(IS_BIG_ENDIAN)?; + let method_id = method_id & (!0x8000); + + let mut data: Vec = vec![0u8; (size - 2 - 4 - 4) as _]; + + stream.read(&mut data)?; + + + RMCResponseResult::Success { + call_id, + method_id, + data + } + } else { + let error_code: u32 = stream.read_struct(IS_BIG_ENDIAN)?; + let error_code = error_code & (!0x80000000); + let call_id: u32 = stream.read_struct(IS_BIG_ENDIAN)?; + + RMCResponseResult::Error { + error_code: { + match ErrorCode::try_from(error_code){ + Ok(v) => v, + Err(e) => { + error!("invalid error code {:#010x}", error_code); + Core_Exception + } + } + }, + call_id, + + } + }; + + Ok(Self{ + protocol_id, + response_result + }) + } + + pub fn get_call_id(&self) -> u32{ + match &self.response_result{ + RMCResponseResult::Success { call_id, ..} => *call_id, + RMCResponseResult::Error { call_id, .. } => *call_id + } + } + + pub fn to_data(self) -> Vec { generate_response(self.protocol_id, self.response_result).expect("failed to generate response") } } -pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Result>{ - let size = 1 + 1 + match &response{ +pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Result> { + let size = 1 + 1 + match &response { RMCResponseResult::Success { data, .. } => 4 + 4 + data.len(), - RMCResponseResult::Error{..} => 4 + 4, + RMCResponseResult::Error { .. } => 4 + 4, }; let mut data_out = Vec::with_capacity(size + 4); @@ -48,7 +118,7 @@ pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Re data_out.write_all(bytes_of(&u32_size))?; data_out.push(protocol_id); - match response{ + match response { RMCResponseResult::Success { call_id, method_id, @@ -59,7 +129,7 @@ pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Re let ored_method_id = method_id | 0x8000; data_out.write_all(bytes_of(&ored_method_id))?; data_out.write_all(&data)?; - }, + } RMCResponseResult::Error { call_id, error_code @@ -76,38 +146,46 @@ pub fn generate_response(protocol_id: u8, response: RMCResponseResult) -> io::Re Ok(data_out) } -pub async fn send_response(original_packet: &PRUDPPacket, socket: &SocketData, connection: &mut ConnectionData, rmcresponse: RMCResponse){ - let ConnectionData{ - active_connection_data, - .. - } = connection; - - let Some(active_connection) = active_connection_data else { - return; +pub async fn send_result( + connection: &SendingConnection, + result: Result, ErrorCode>, + protocol_id: u8, + method_id: u32, + call_id: u32, +) { + + println!("{}", hex::encode(result.clone().unwrap())); + let response_result = match result { + Ok(v) => RMCResponseResult::Success { + call_id, + method_id, + data: v + }, + Err(e) => + RMCResponseResult::Error { + call_id, + error_code: e.into() + } }; - let mut packet = original_packet.base_response_packet(); + let response = RMCResponse{ + response_result, + protocol_id + }; - - packet.header.types_and_flags.set_types(DATA); - packet.header.types_and_flags.set_flag((original_packet.header.types_and_flags.get_flags() & RELIABLE) | NEED_ACK); - - packet.header.session_id = active_connection.server_session_id; - packet.header.substream_id = 0; - - packet.options.push(FragmentId(0)); - - packet.payload = rmcresponse.to_data(); - - //tokio::time::sleep(Duration::from_millis(500)).await; - - connection.finish_and_send_packet_to(socket, packet).await; + send_response(connection, response).await } +pub async fn send_response(connection: &SendingConnection, rmcresponse: RMCResponse) { + connection.send(rmcresponse.to_data()).await; +} + + //taken from kinnays error list directly #[allow(nonstandard_style)] #[repr(u32)] +#[derive(Debug, EnumTryInto, Clone, Copy)] pub enum ErrorCode { Core_Unknown = 0x00010001, Core_NotImplemented = 0x00010002, @@ -379,25 +457,25 @@ pub enum ErrorCode { Custom_Unknown = 0x00740001, Ess_Unknown = 0x00750001, Ess_GameSessionError = 0x00750002, - Ess_GameSessionMaintenance = 0x00750003 + Ess_GameSessionMaintenance = 0x00750003, } -impl Into for ErrorCode { +impl Into for ErrorCode { fn into(self) -> u32 { - unsafe{ transmute(self) } + unsafe { transmute(self) } } } #[cfg(test)] -mod test{ +mod test { use hmac::digest::consts::U5; use hmac::digest::KeyInit; use rc4::{Rc4, StreamCipher}; use crate::rmc::response::ErrorCode; #[test] - fn test(){ - let mut data_orig = [0,1,2,3,4,5,6,7,8,9,69,4,20]; + fn test() { + let mut data_orig = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 69, 4, 20]; let mut data = data_orig; let mut rc4: Rc4 = @@ -413,11 +491,10 @@ mod test{ rc4.apply_keystream(&mut data); assert_eq!(data_orig, data); - } #[test] - fn test_enum_equivilance(){ + fn test_enum_equivilance() { let val: u32 = ErrorCode::Core_Unknown.into(); assert_eq!(val, 0x00010001) } diff --git a/src/rmc/structures/any.rs b/src/rmc/structures/any.rs index cc1de19..d402f3e 100644 --- a/src/rmc/structures/any.rs +++ b/src/rmc/structures/any.rs @@ -2,15 +2,24 @@ use std::io::{Read, Write}; use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; use super::{Result, RmcSerialize}; -#[derive(Debug)] +#[derive(Debug, Default)] pub struct Any{ pub name: String, pub data: Vec } impl RmcSerialize for Any{ - fn serialize(&self, _writer: &mut dyn Write) -> Result<()> { - todo!() + fn serialize(&self, writer: &mut dyn Write) -> Result<()> { + self.name.serialize(writer)?; + + let u32_len = self.data.len() as u32; + + u32_len.serialize(writer)?; + u32_len.serialize(writer)?; + + self.data.serialize(writer)?; + + Ok(()) } fn deserialize(mut reader: &mut dyn Read) -> Result { let name = String::deserialize(reader)?; diff --git a/src/rmc/structures/buffer.rs b/src/rmc/structures/buffer.rs index aea5a45..5370cf5 100644 --- a/src/rmc/structures/buffer.rs +++ b/src/rmc/structures/buffer.rs @@ -1,6 +1,8 @@ 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; diff --git a/src/rmc/structures/connection_data.rs b/src/rmc/structures/connection_data.rs index de20491..1adbb20 100644 --- a/src/rmc/structures/connection_data.rs +++ b/src/rmc/structures/connection_data.rs @@ -1,29 +1,15 @@ use std::io::{Read, Write}; use bytemuck::bytes_of; +use macros::RmcSerialize; use crate::kerberos::KerberosDateTime; use crate::rmc::structures::{rmc_struct, RmcSerialize}; -pub struct ConnectionData<'a>{ - pub station_url: &'a str, +#[derive(Debug, RmcSerialize)] +#[rmc_struct(1)] +pub struct ConnectionData{ + pub station_url: String, pub special_protocols: Vec, - pub special_station_url: &'a str, + pub special_station_url: String, pub date_time: KerberosDateTime } -impl<'a> RmcSerialize for ConnectionData<'a>{ - fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { - rmc_struct::write_struct(writer, 1, |v|{ - self.station_url.serialize(v).expect("unable to write station url"); - self.special_protocols.serialize(v).expect("unable to write special protocols"); - self.special_station_url.serialize(v).expect("unable to write special station url"); - v.write_all(bytes_of(&self.date_time)).expect("unable to write date time"); - - Ok(()) - }) - } - - fn deserialize(_reader: &mut dyn Read) -> crate::rmc::structures::Result { - todo!() - } -} - diff --git a/src/rmc/structures/list.rs b/src/rmc/structures/list.rs index 7f6c97c..6f6b456 100644 --- a/src/rmc/structures/list.rs +++ b/src/rmc/structures/list.rs @@ -4,6 +4,9 @@ use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; use crate::rmc::structures::RmcSerialize; + +// this is also for implementing `Buffer` this is tecnically not the same as its handled internaly +// probably but as it has the same mapping it doesn't matter and simplifies things impl RmcSerialize for Vec{ fn serialize(&self, writer: &mut dyn Write) -> crate::rmc::structures::Result<()> { let u32_len = self.len() as u32; diff --git a/src/rmc/structures/matchmake.rs b/src/rmc/structures/matchmake.rs index 72cb6a0..f11b432 100644 --- a/src/rmc/structures/matchmake.rs +++ b/src/rmc/structures/matchmake.rs @@ -3,7 +3,7 @@ use crate::kerberos::KerberosDateTime; use crate::rmc::structures::variant::Variant; // rmc structure -#[derive(RmcSerialize, Debug, Clone)] +#[derive(RmcSerialize, Debug, Clone, Default)] #[rmc_struct(0)] pub struct Gathering { pub self_gid: u32, @@ -19,7 +19,7 @@ pub struct Gathering { } // rmc structure -#[derive(RmcSerialize, Debug, Clone)] +#[derive(RmcSerialize, Debug, Clone, Default)] #[rmc_struct(0)] pub struct MatchmakeParam { pub params: Vec<(String, Variant)>, @@ -27,7 +27,7 @@ pub struct MatchmakeParam { // rmc structure -#[derive(RmcSerialize, Debug, Clone)] +#[derive(RmcSerialize, Debug, Clone, Default)] #[rmc_struct(3)] pub struct MatchmakeSession { //inherits from @@ -81,4 +81,16 @@ pub struct AutoMatchmakeParam { pub participation_count: u16, pub search_criteria: Vec, pub target_gids: Vec, -} \ No newline at end of file +} + +#[derive(RmcSerialize, Debug, Clone)] +#[rmc_struct(0)] +pub struct CreateMatchmakeSessionParam { + pub matchmake_session: MatchmakeSession, + pub additional_participants: Vec, + pub gid_for_participation_check: u32, + pub create_matchmake_session_option: u32, + pub join_message: String, + pub participation_count: u16, + +} diff --git a/src/rmc/structures/mod.rs b/src/rmc/structures/mod.rs index fc4e9d9..a9f9ab6 100644 --- a/src/rmc/structures/mod.rs +++ b/src/rmc/structures/mod.rs @@ -17,7 +17,7 @@ pub enum Error{ VersionMismatch(u8), } -pub(crate) type Result = std::result::Result; +pub type Result = std::result::Result; pub mod string; pub mod any; @@ -30,8 +30,18 @@ pub mod qbuffer; pub mod primitives; pub mod matchmake; pub mod variant; +pub mod ranking; pub trait RmcSerialize: Sized{ fn serialize(&self, writer: &mut dyn Write) -> Result<()>; fn deserialize(reader: &mut dyn Read) -> Result; +} + +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/primitives.rs b/src/rmc/structures/primitives.rs index bd08da8..e8855f0 100644 --- a/src/rmc/structures/primitives.rs +++ b/src/rmc/structures/primitives.rs @@ -91,4 +91,112 @@ impl RmcSerialize for (T, U){ 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)) + } } \ No newline at end of file diff --git a/src/rmc/structures/qbuffer.rs b/src/rmc/structures/qbuffer.rs index 09fc8e3..f87c49e 100644 --- a/src/rmc/structures/qbuffer.rs +++ b/src/rmc/structures/qbuffer.rs @@ -1,12 +1,30 @@ -use std::io::Read; +use std::io::{Read, Write}; +use bytemuck::bytes_of; use crate::endianness::{IS_BIG_ENDIAN, ReadExtensions}; -use crate::rmc::structures::Result; -pub fn read(reader: &mut impl Read) -> Result>{ - let size: u16 = reader.read_struct(IS_BIG_ENDIAN)?; +use crate::rmc::structures::{Result, RmcSerialize}; +use crate::rmc::structures::qresult::QResult; - let mut vec = vec![0; size as usize]; - reader.read_exact(&mut vec)?; +#[derive(Debug)] +pub struct QBuffer(pub Vec); - Ok(vec) +impl RmcSerialize for QBuffer{ + fn serialize(&self, writer: &mut dyn Write) -> Result<()> { + let len_u16 = self.0.len() as u16; + + writer.write(bytes_of(&len_u16))?; + writer.write(&self.0)?; + + Ok(()) + } + + fn deserialize(mut reader: &mut dyn Read) -> Result { + let size: u16 = reader.read_struct(IS_BIG_ENDIAN)?; + + let mut vec = vec![0; size as usize]; + + reader.read_exact(&mut vec)?; + + Ok(Self(vec)) + } } \ No newline at end of file diff --git a/src/rmc/structures/qresult.rs b/src/rmc/structures/qresult.rs index d4a5078..41a5263 100644 --- a/src/rmc/structures/qresult.rs +++ b/src/rmc/structures/qresult.rs @@ -7,7 +7,7 @@ use crate::rmc::structures::{RmcSerialize, Result}; pub const ERROR_MASK: u32 = 1 << 31; -#[derive(Pod, Zeroable, Copy, Clone, SwapEndian)] +#[derive(Pod, Zeroable, Copy, Clone, SwapEndian, Debug)] #[repr(transparent)] pub struct QResult(u32); diff --git a/src/rmc/structures/ranking.rs b/src/rmc/structures/ranking.rs new file mode 100644 index 0000000..c912d3f --- /dev/null +++ b/src/rmc/structures/ranking.rs @@ -0,0 +1,69 @@ +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 index 2477315..8f51a5a 100644 --- a/src/rmc/structures/rmc_struct.rs +++ b/src/rmc/structures/rmc_struct.rs @@ -17,7 +17,7 @@ pub fn write_struct(writer: &mut dyn Write, version: u8, pred: impl FnOnce(&mut (pred)(&mut scratch_space)?; - let u32_size= scratch_space.len() as u32; + let u32_size = scratch_space.len() as u32; writer.write_all(bytes_of(&u32_size))?; writer.write_all(&scratch_space)?; diff --git a/src/rmc/structures/string.rs b/src/rmc/structures/string.rs index 712da2c..690e6a1 100644 --- a/src/rmc/structures/string.rs +++ b/src/rmc/structures/string.rs @@ -35,4 +35,5 @@ impl RmcSerialize for &str{ Ok(()) } -} \ No newline at end of file +} + diff --git a/src/rmc/structures/variant.rs b/src/rmc/structures/variant.rs index 7472b03..ebd25b8 100644 --- a/src/rmc/structures/variant.rs +++ b/src/rmc/structures/variant.rs @@ -3,8 +3,9 @@ use crate::kerberos::KerberosDateTime; use crate::rmc::structures; use crate::rmc::structures::RmcSerialize; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub enum Variant{ + #[default] None, SInt64(i64), Double(f64), diff --git a/src/versions.rs b/src/versions.rs new file mode 100644 index 0000000..0fa85fb --- /dev/null +++ b/src/versions.rs @@ -0,0 +1,97 @@ +use std::marker::PhantomData; +use std::ops::{BitAnd, BitOr}; +use typenum::{Cmp, IsEqual, IsLess, IsLessOrEqual, Unsigned, U1, U2, U3}; + +/// This trait represents a version at compile time +trait Version{ + type Major: Unsigned; + type Minor: Unsigned; +} + +/// This struct contains nothing and is used to represent specific versions as an instance of +/// [`Version`]. It is instances as `Ver` +struct Ver{ + _phantom: PhantomData<(MAJ, MIN)> +} + +impl Version for Ver{ + type Major = MAJ; + type Minor = MIN; +} + +/// Represents two versions which can be compared +trait ComparableVersion: Version{ + type IsAtLeast: SameOrUnit; +} + +impl ComparableVersion for U where + ::Major: Cmp, + ::Minor: IsLessOrEqual, + ::Major: IsEqual< + Self::Major, + Output: BitAnd< + typenum::LeEq + >, + >, + ::Major: IsLess< + Self::Major, + Output: BitOr< + typenum::And< + typenum::Eq, + typenum::LeEq, + >, + Output: SameOrUnit + > + > { + + type IsAtLeast = typenum::Or< + typenum::Le, + typenum::And< + typenum::Eq, + typenum::LeEq, + > + >; +} + + +/// Simple check for testing if the `TEST` version is at least `REQ` or higher. +type VersionAbove = >::IsAtLeast; + +trait VersionIsAtLeast{} + +impl> VersionIsAtLeast for T{} + + +/// Trait for containing the result of elements which only conditionally exist +trait CondElemResult{ + type Output; +} + +/// Empty helper struct which only servers to give a concrete type when creating fields in rmc +/// structs which have a version requirement. This is not meant to be used directly, use +/// [`MinVersion`] instead. +struct MinVersionElementHelper>{ + _phantom: PhantomData<(T, REQUIRED, VER)> +} + +/// This should be used either with [`typenum::True`] or [`typenum::False`]. When `True` the [`Self::Output`] +/// will be the same as the `T` you put into Output. When `False` it will always be `()` +trait SameOrUnit{ + type Output; +} + +impl SameOrUnit for typenum::True{ + type Output = T; +} + +impl SameOrUnit for typenum::False{ + type Output = (); +} + +impl> CondElemResult for MinVersionElementHelper where { + type Output = <>::IsAtLeast as SameOrUnit>::Output; +} + +/// When the version condition is met the field will exist and will simply be `T` if not it will be +/// replaced by `()`. Use this when you need to add versioning to rmc structs. +type MinVersion = as CondElemResult>::Output; \ No newline at end of file diff --git a/src/web/mod.rs b/src/web/mod.rs new file mode 100644 index 0000000..3461257 --- /dev/null +++ b/src/web/mod.rs @@ -0,0 +1,37 @@ +use std::net::SocketAddrV4; +use once_cell::sync::Lazy; +use rocket::{get, routes, Rocket}; +use rocket::serde::json::Json; +use tokio::task::JoinHandle; +use serde::Serialize; +use tokio::sync::Mutex; + +#[get("/")] +async fn server_data() -> Json { + Json(WEB_DATA.lock().await.clone()) +} + +pub async fn start_web() -> JoinHandle<()>{ + tokio::spawn(async{ + rocket::build() + .mount("/",routes![server_data]) + .launch().await + .expect("unable to start webserver"); + }) +} +#[derive(Serialize, Clone)] +pub enum DirectionalData{ + Incoming(String), + Outgoing(String) +} + +#[derive(Serialize, Default, Clone)] +pub struct WebData{ + pub data: Vec<(SocketAddrV4, DirectionalData)> +} + +pub static WEB_DATA: Lazy> = Lazy::new(|| Mutex::new( + WebData{ + data: Vec::new(), + } +)); \ No newline at end of file