diff --git a/Cargo.lock b/Cargo.lock index b7ebf9f..b010331 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,7 +149,7 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -162,12 +162,31 @@ dependencies = [ "inout", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -187,6 +206,12 @@ dependencies = [ "typenum", ] +[[package]] +name = "data-encoding" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" + [[package]] name = "deranged" version = "0.4.0" @@ -229,6 +254,50 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -250,6 +319,18 @@ dependencies = [ "wasi", ] +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + [[package]] name = "gimli" version = "0.31.1" @@ -471,6 +552,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "openssl-probe" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391" + [[package]] name = "parking_lot" version = "0.12.4" @@ -512,6 +599,12 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "powerfmt" version = "0.2.0" @@ -543,6 +636,7 @@ dependencies = [ "cfg-if", "log", "proxy-common", + "prudplite", "prudpv0", "prudpv1", "rnex-core", @@ -559,6 +653,20 @@ dependencies = [ "tokio", ] +[[package]] +name = "prudplite" +version = "0.1.0" +dependencies = [ + "bytemuck", + "futures-util", + "log", + "proxy-common", + "rnex-core", + "tokio", + "tokio-tungstenite", + "v-byte-helpers", +] + [[package]] name = "prudpv0" version = "0.1.0" @@ -604,6 +712,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "rand" version = "0.8.5" @@ -611,8 +725,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", ] [[package]] @@ -622,7 +746,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", ] [[package]] @@ -631,7 +765,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", ] [[package]] @@ -660,7 +803,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -683,7 +826,7 @@ dependencies = [ "md-5", "once_cell", "paste", - "rand", + "rand 0.8.5", "rc4", "simplelog", "thiserror", @@ -714,6 +857,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pki-types" version = "1.12.0" @@ -740,12 +895,44 @@ version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.219" @@ -766,6 +953,17 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" @@ -941,6 +1139,51 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", +] + +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.2", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror", + "utf-8", +] + [[package]] name = "typenum" version = "1.18.0" @@ -1015,6 +1258,15 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -1099,7 +1351,7 @@ checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", - "windows-link", + "windows-link 0.1.3", "windows-result", "windows-strings", ] @@ -1132,13 +1384,19 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-result" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -1147,7 +1405,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -1168,6 +1426,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -1232,6 +1499,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + [[package]] name = "zerocopy" version = "0.8.26" diff --git a/Cargo.toml b/Cargo.toml index 9d6ea8a..046a20f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,4 @@ members = [ "rnex-core", "prudpv1", "prudpv0" -, "proxy", "proxy-common"] +, "proxy", "proxy-common", "prudplite"] diff --git a/editions.yaml b/editions.yaml index 1c025b0..5458ec6 100644 --- a/editions.yaml +++ b/editions.yaml @@ -1,7 +1,17 @@ splatoon: features: - - rmc_struct_header - prudpv1 + - v3-8-15 + settings: + AUTH_REPORT_VERSION: "branch:origin/project/wup-agmj build:3_8_15_2004_0" + RNEX_VIRTUAL_PORT_INSECURE: "1:10" + RNEX_VIRTUAL_PORT_SECURE: "1:10" + RNEX_DEFAULT_PORT: 6000 +splatoon2: + features: + - v4-3-11 + - prudplite + - nx settings: AUTH_REPORT_VERSION: "branch:origin/project/wup-agmj build:3_8_15_2004_0" RNEX_VIRTUAL_PORT_INSECURE: "1:10" diff --git a/macros/Cargo.toml b/macros/Cargo.toml index dca135c..f748300 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "macros" version = "0.0.0" -authors = ["DJMrTV "] +authors = ["RusticMaple "] description = "A `cargo generate` template for quick-starting a procedural macro crate" keywords = ["template", "proc_macro", "procmacro"] edition = "2018" @@ -14,4 +14,3 @@ doctest = false quote = "1.0.38" proc-macro2 = "1.0.93" syn = { version = "2.0.98", features = ["full"] } - diff --git a/macros/src/lib.rs b/macros/src/lib.rs index d088561..d0fc909 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -48,7 +48,11 @@ impl Parse for ProtoInputParams { fn gen_serialize_data_struct( s: DataStruct, struct_attr: Option<&Attribute>, -) -> (proc_macro2::TokenStream, proc_macro2::TokenStream, proc_macro2::TokenStream) { +) -> ( + proc_macro2::TokenStream, + proc_macro2::TokenStream, + proc_macro2::TokenStream, +) { let serialize_base_content = { let mut serialize_content = quote! {}; @@ -123,8 +127,6 @@ fn gen_serialize_data_struct( let mut size_content = quote! { 0 }; for f in &s.fields { - - let ident = f.ident.as_ref().unwrap(); size_content.append_all(quote! { @@ -135,12 +137,11 @@ fn gen_serialize_data_struct( size_content }; let write_size = if let Some(_) = struct_attr { - quote!{ #write_size + if rnex_core::config::FEATURE_HAS_STRUCT_HEADER{ 5 } else { 0 } } + quote! { #write_size + if rnex_core::config::FEATURE_HAS_STRUCT_HEADER{ 5 } else { 0 } } } else { write_size }; - // generate base with extends stuff let serialize_base_content = if let Some(attr) = struct_attr { @@ -215,7 +216,7 @@ fn gen_serialize_data_struct( deserialize_base_content }; - let write_size = quote!{ + let write_size = quote! { fn serialize_write_size(&self) -> rnex_core::rmc::structures::Result{ Ok(#write_size) } @@ -390,7 +391,7 @@ pub fn rmc_serialize(input: TokenStream) -> TokenStream { Ok(val) }; - (serialize_base_content, deserialize_base_content, quote!{}) + (serialize_base_content, deserialize_base_content, quote! {}) } Data::Union(_) => { unimplemented!() diff --git a/macros/src/protos.rs b/macros/src/protos.rs index 7dd7bf1..2e95e71 100644 --- a/macros/src/protos.rs +++ b/macros/src/protos.rs @@ -1,47 +1,46 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote, ToTokens}; -use syn::{LitInt, LitStr, ReturnType, Type}; use syn::token::{Brace, Paren, Semi}; +use syn::{Attribute, LitInt, LitStr, ReturnType, Type}; -pub struct ProtoMethodData{ +pub struct ProtoMethodData { pub id: LitInt, pub name: Ident, pub parameters: Vec<(Ident, Type)>, pub ret_val: ReturnType, } - /// This is a representation of the code generated by `rmc_proto` it serves to split the logic of /// acquiring data from the actual generation to tidy up the process into first getting then /// generating. /// /// Use the [`ToTokens`] trait to generate the actual code. -pub struct RmcProtocolData{ +pub struct RmcProtocolData { pub has_returns: bool, pub id: LitInt, pub name: Ident, - pub methods: Vec + pub methods: Vec, } -impl RmcProtocolData{ - fn generate_raw_trait(&self, tokens: &mut TokenStream){ - let Self{ +impl RmcProtocolData { + fn generate_raw_trait(&self, tokens: &mut TokenStream) { + let Self { has_returns, name, id, - methods + methods, } = self; // this gives us the name which the identifier of the corresponding Raw trait let raw_name = Ident::new(&format!("Raw{}", name), name.span()); - // boilerplate tokens which all raw traits need - quote!{ + quote! { #[doc(hidden)] #[allow(unused_must_use)] pub trait #raw_name: #name - }.to_tokens(tokens); + } + .to_tokens(tokens); // generate the body of the raw protocol trait Brace::default().surround(tokens, |tokens|{ @@ -191,9 +190,10 @@ impl RmcProtocolData{ }); }); - quote!{ + quote! { impl #raw_name for T{} - }.to_tokens(tokens); + } + .to_tokens(tokens); } fn generate_raw_remote_trait(&self, tokens: &mut TokenStream) { @@ -208,13 +208,13 @@ impl RmcProtocolData{ // this gives us the name which the identifier of the corresponding Raw trait let remote_name = Ident::new(&format!("Remote{}", name), name.span()); - // boilerplate tokens which all raw traits need - quote!{ + quote! { #[doc(hidden)] #[allow(unused_must_use)] pub trait #remote_name: rnex_core::rmc::protocols::HasRmcConnection - }.to_tokens(tokens); + } + .to_tokens(tokens); // generate the body of the raw protocol trait Brace::default().surround(tokens, |tokens|{ @@ -299,35 +299,29 @@ impl RmcProtocolData{ }) } }); - - } - fn generate_raw_info(&self, tokens: &mut TokenStream){ - let Self{ - name, - id, - .. - } = self; + fn generate_raw_info(&self, tokens: &mut TokenStream) { + let Self { name, id, .. } = self; let raw_info_name = Ident::new(&format!("Raw{}Info", name), Span::call_site()); - - quote!{ + quote! { #[doc(hidden)] pub struct #raw_info_name; impl #raw_info_name { pub const PROTOCOL_ID: u16 = #id; } - }.to_tokens(tokens); + } + .to_tokens(tokens); } } -impl ToTokens for RmcProtocolData{ +impl ToTokens for RmcProtocolData { fn to_tokens(&self, tokens: &mut TokenStream) { self.generate_raw_trait(tokens); self.generate_raw_info(tokens); self.generate_raw_remote_trait(tokens); } -} \ No newline at end of file +} diff --git a/proxy-common/src/lib.rs b/proxy-common/src/lib.rs index 93c16c8..3bd902d 100644 --- a/proxy-common/src/lib.rs +++ b/proxy-common/src/lib.rs @@ -1,5 +1,6 @@ use log::{error, info}; use rnex_core::{ + PID, executables::common::{OWN_IP_PUBLIC, try_get_ip}, prudp::{socket_addr::PRUDPSockAddr, virtual_port::VirtualPort}, reggie::{RemoteEdgeNodeHolder, UnitPacketWrite}, @@ -83,7 +84,7 @@ impl ProxyStartupParam { let self_public: SocketAddrV4 = match try_get_env("SERVER_IP_PUBLIC") { Ok(v) => v, Err(e) => try_get_ip() - .map(|v| SocketAddrV4::new(v, RNEX_DEFAULT_PORT)) + .map(|v| SocketAddrV4::new(v, self_private.port())) .map_err(move |v| Error::PubAddrGetErr(Box::new(e), v))?, }; @@ -175,7 +176,7 @@ pub async fn setup_edge_node_connection( pub async fn new_backend_connection( param: &ProxyStartupParam, addr: PRUDPSockAddr, - pid: u32, + pid: PID, ) -> Option { info!("attempting to connect to: {}", param.forward_destination); let mut stream = match TcpStream::connect(param.forward_destination).await { diff --git a/proxy/Cargo.toml b/proxy/Cargo.toml index fa56ab2..98e8b2b 100644 --- a/proxy/Cargo.toml +++ b/proxy/Cargo.toml @@ -7,6 +7,7 @@ edition = "2024" tokio = { version = "1.47.0", features = ["full"] } prudpv0 = { path = "../prudpv0", optional = true } prudpv1 = { path = "../prudpv1", optional = true } +prudplite = { path = "../prudplite", optional = true } proxy-common = { path = "../proxy-common" } cfg-if = "1.0.4" rnex-core = { path = "../rnex-core", version = "0.1.1" } @@ -15,6 +16,7 @@ log = "0.4.25" [features] prudpv0 = ["dep:prudpv0"] prudpv1 = ["dep:prudpv1"] +prudplite = ["dep:prudplite"] friends = ["prudpv0", "prudpv0/friends"] splatoon = ["prudpv1"] diff --git a/proxy/src/lib.rs b/proxy/src/lib.rs index 2c52a9a..ea5b02c 100644 --- a/proxy/src/lib.rs +++ b/proxy/src/lib.rs @@ -8,6 +8,8 @@ cfg_if! { pub use prudpv0::*; } else if #[cfg(feature = "prudpv1")] { pub use prudpv1::*; + } else if #[cfg(feature = "prudplite")]{ + pub use prudplite::*; } else { compile_error!("no proxy type has been set"); } diff --git a/prudplite/Cargo.toml b/prudplite/Cargo.toml new file mode 100644 index 0000000..902b1a7 --- /dev/null +++ b/prudplite/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "prudplite" +version = "0.1.0" +edition = "2024" + +[features] +nx = [] +v4-3-11 = [] + +[dependencies] +rnex-core = { path = "../rnex-core", version = "0.1.1" } +tokio = { version = "1.47.0", features = ["full"] } +bytemuck = { version = "1.23.1", features = ["derive"] } +proxy-common = {path = "../proxy-common"} +tokio-tungstenite = {version = "0.28.0", features = ["rustls", "rustls-tls-native-roots"]} +log = "0.4.25" +futures-util = "0.3.31" +v-byte-helpers = { git = "https://github.com/RusticMaple/VByteMacros", version = "0.1.1" } diff --git a/prudplite/src/crypto/insecure.rs b/prudplite/src/crypto/insecure.rs new file mode 100644 index 0000000..34d0f93 --- /dev/null +++ b/prudplite/src/crypto/insecure.rs @@ -0,0 +1,14 @@ +use rnex_core::PID; + +use crate::crypto::Crypto; + +pub struct Insecure; + +impl Crypto for Insecure { + fn new_connection(&self, data: &[u8]) -> Option<(PID, Vec)> { + Some((100, vec![])) + } + fn new() -> Self { + Self + } +} diff --git a/prudplite/src/crypto/mod.rs b/prudplite/src/crypto/mod.rs new file mode 100644 index 0000000..38d8091 --- /dev/null +++ b/prudplite/src/crypto/mod.rs @@ -0,0 +1,9 @@ +use rnex_core::PID; + +pub mod insecure; +pub mod secure; + +pub trait Crypto: 'static + Send + Sync { + fn new_connection(&self, data: &[u8]) -> Option<(PID, Vec)>; + fn new() -> Self; +} diff --git a/prudplite/src/crypto/secure.rs b/prudplite/src/crypto/secure.rs new file mode 100644 index 0000000..4aff759 --- /dev/null +++ b/prudplite/src/crypto/secure.rs @@ -0,0 +1,27 @@ +use rnex_core::{ + PID, executables::common::SECURE_SERVER_ACCOUNT, nex::account::Account, + prudp::ticket::read_secure_connection_data, rmc::structures::RmcSerialize, +}; + +use crate::crypto::Crypto; + +pub struct Secure(&'static Account); + +impl Crypto for Secure { + fn new_connection(&self, data: &[u8]) -> Option<(PID, Vec)> { + let (_, pid, check_value) = read_secure_connection_data(data, &self.0)?; + + let check_value_response = check_value + 1; + + let data = bytemuck::bytes_of(&check_value_response); + + let mut response = Vec::new(); + + data.serialize(&mut response).ok()?; + + Some((pid, response)) + } + fn new() -> Self { + Self(&SECURE_SERVER_ACCOUNT) + } +} diff --git a/prudplite/src/lib.rs b/prudplite/src/lib.rs new file mode 100644 index 0000000..05938d7 --- /dev/null +++ b/prudplite/src/lib.rs @@ -0,0 +1,309 @@ +pub mod crypto; +mod packet; + +use std::{collections::HashMap, net::SocketAddr, sync::Arc}; + +use crate::{ + crypto::{Crypto, insecure::Insecure, secure::Secure}, + packet::{LiteHeader, LitePacket, PacketSpecificData, StreamTypes, create_packet_from}, +}; +use futures_util::{SinkExt, StreamExt}; +use log::{error, info, warn}; +use proxy_common::{ProxyStartupParam, new_backend_connection}; +use rnex_core::{ + PID, + prudp::{ + socket_addr::PRUDPSockAddr, + types_flags::{ + TypesFlags, + flags::{ACK, NEED_ACK, RELIABLE}, + types::{CONNECT, DATA, DISCONNECT, SYN}, + }, + virtual_port::VirtualPort, + }, + util::SplittableBufferConnection, +}; +use tokio::net::{TcpListener, TcpStream}; +use tokio_tungstenite::{ + WebSocketStream, + tungstenite::{ + Bytes, Message, client::IntoClientRequest, http::header::ACCESS_CONTROL_REQUEST_METHOD, + }, +}; + +struct ConnectionState { + param: Arc, + active: bool, + websocket: WebSocketStream, + pid: PID, + backend_conn: SplittableBufferConnection, + addr: PRUDPSockAddr, + incoming_reliable: HashMap>, + client_reliable_counter: u16, + server_reliable_counter: u16, +} + +impl ConnectionState { + pub async fn handle_incoming_prudp(&mut self, packet: LitePacket, sorted: bool) { + let Some(header) = packet.header() else { + warn!("invalid data on connection"); + return; + }; + + if (header.types_flags.get_flags() & NEED_ACK) != 0 { + let data = create_packet_from( + LiteHeader { + stream_types: StreamTypes::new( + self.param.virtual_port.get_stream_type(), + self.addr.virtual_port.get_stream_type(), + ), + source_port: self.param.virtual_port.get_port_number(), + destination_port: self.addr.virtual_port.get_port_number(), + fragment_id: header.fragment_id, + types_flags: TypesFlags::default() + .types(header.types_flags.get_types()) + .flags(ACK), + sequence_id: header.sequence_id, + ..Default::default() + }, + &[], + &[], + ); + let data: Bytes = data.into(); + if header.types_flags.get_types() == DISCONNECT { + self.websocket.send(Message::Binary(data.clone())).await; + self.websocket.send(Message::Binary(data.clone())).await; + } + self.websocket.send(Message::Binary(data)).await; + } + + if (header.types_flags.get_flags() & ACK) != 0 { + // we can just safely ignore acks, we ARE sending over tcp after all already guarantees that our packets will arrive + // we can however not guarantee the order of incoming client packets so we should still take care of that + // (the client might be doing some funny things which we dont know of) + return; + } + + if (header.types_flags.get_flags() & RELIABLE != 0) & !sorted { + self.incoming_reliable.insert(header.sequence_id, packet); + if self.incoming_reliable.len() > 5 { + self.active = false; + warn!("client is spamming out of order reliable packets, throwing out"); + } + return; + } + + match header.types_flags.get_types() { + DATA => { + if header.fragment_id != 0 { + warn!("fragmented packets arent yet supported"); + return; + } + + let Some(payload) = packet.payload() else { + return; + }; + self.backend_conn.send(payload.into()).await; + } + PING => {} + v => { + info!("unimplemented packet type: {}", v); + } + } + } + pub async fn process_reliable(&mut self) { + while let Some(v) = self.incoming_reliable.remove(&self.client_reliable_counter) { + self.handle_incoming_prudp(v, true).await; + self.client_reliable_counter += 1; + } + } + pub async fn handle_connection(&mut self) { + while self.active { + tokio::select! { + v = self.websocket.next() => { + match v { + Some(Ok(Message::Binary(v))) => { + self.handle_incoming_prudp(LitePacket::new(v), false).await; + } + _ => { + info!("client disconnected or errored out"); + return; + } + } + } + v = self.backend_conn.recv() => { + + } + } + } + } +} + +pub async fn websocket_thread_unconnected( + param: Arc, + crypto: Arc, + conn: TcpStream, + addr: SocketAddr, +) { + let mut websocket = match tokio_tungstenite::accept_async(conn).await { + Ok(v) => v, + Err(e) => { + error!("error accepting websocket connection: {}", e); + return; + } + }; + + while let Some(Ok(v)) = websocket.next().await { + match v { + Message::Binary(b) => { + let packet = LitePacket::new(b); + + let Some(header) = packet.header() else { + error!("got malformed message, disconnecting"); + return; + }; + + match header.types_flags.get_types() { + SYN => { + let Some(supported) = packet.packet_specific_iter() else { + error!("got malformed message, disconnecting"); + return; + }; + + let Some(PacketSpecificData::SupportedFunctions(s)) = supported + .into_iter() + .find(|v| matches!(v, PacketSpecificData::SupportedFunctions(_))) + else { + error!("got malformed message, disconnecting"); + return; + }; + + let data = create_packet_from( + LiteHeader { + destination_port: header.source_port, + source_port: param.virtual_port.get_port_number(), + stream_types: StreamTypes::new( + param.virtual_port.get_stream_type(), + header.stream_types.source(), + ), + fragment_id: 0, + sequence_id: 0, + types_flags: TypesFlags::default().types(SYN).flags(ACK), + ..Default::default() + }, + &[ + PacketSpecificData::SupportedFunctions(s & 0xFF), + PacketSpecificData::ConnectionSignature([0; 16]), + ], + &[], + ); + websocket.send(Message::Binary(data.into())).await; + } + CONNECT => { + let Some(supported) = packet.packet_specific_iter() else { + error!("got malformed message, disconnecting"); + return; + }; + + let Some(PacketSpecificData::SupportedFunctions(s)) = supported + .into_iter() + .find(|v| matches!(v, PacketSpecificData::SupportedFunctions(_))) + else { + error!("got malformed message, disconnecting"); + return; + }; + + let Some(data) = packet.payload() else { + error!("got malformed message, disconnecting"); + return; + }; + + let Some((pid, data)) = crypto.new_connection(data) else { + error!("invalid login data"); + return; + }; + + let data = create_packet_from( + LiteHeader { + destination_port: header.source_port, + source_port: param.virtual_port.get_port_number(), + stream_types: StreamTypes::new( + param.virtual_port.get_stream_type(), + header.stream_types.source(), + ), + fragment_id: 0, + sequence_id: 0, + types_flags: TypesFlags::default().types(CONNECT).flags(ACK), + ..Default::default() + }, + &[ + PacketSpecificData::SupportedFunctions(s & 0xFF), + PacketSpecificData::ConnectionSignature([0; 16]), + ], + &data, + ); + websocket.send(Message::Binary(data.into())).await; + + let addr = PRUDPSockAddr::new( + addr, + VirtualPort::new(header.source_port, header.stream_types.source()), + ); + let Some(backend_conn) = new_backend_connection(¶m, addr, pid).await + else { + error!("unable to connect to backend"); + return; + }; + let mut connection = ConnectionState { + active: true, + addr, + pid, + backend_conn, + client_reliable_counter: 2, + server_reliable_counter: 1, + param, + incoming_reliable: HashMap::new(), + websocket, + }; + + connection.handle_connection().await; + break; + } + v => { + error!( + "invalid packet type for unconnected client {}, disconnecting", + v, + ); + } + } + } + v => { + error!("non binary message({:?}) , disconnecting", v); + return; + } + } + } +} + +pub async fn start_proxy(param: ProxyStartupParam) { + let param = Arc::new(param); + let crypto = Arc::new(C::new()); + let listener = TcpListener::bind(param.self_private) + .await + .expect("unable to bind to port"); + + while let Ok((connection, addr)) = listener.accept().await { + let param = param.clone(); + let crypto = crypto.clone(); + tokio::spawn(websocket_thread_unconnected( + param, crypto, connection, addr, + )); + } +} + +pub async fn start_secure(param: ProxyStartupParam) { + start_proxy::(param).await; +} + +pub async fn start_insecure(param: ProxyStartupParam) { + start_proxy::(param).await; +} diff --git a/prudplite/src/main.rs b/prudplite/src/main.rs new file mode 100644 index 0000000..b4d8a0e --- /dev/null +++ b/prudplite/src/main.rs @@ -0,0 +1,45 @@ +use futures_util::{SinkExt, StreamExt}; +use rnex_core::prudp::types_flags::{TypesFlags, flags::NEED_ACK, types::SYN}; +use tokio_tungstenite::tungstenite::{Message, client::IntoClientRequest, http::header}; + +use crate::packet::{LiteHeader, LitePacket, PacketSpecificData, StreamTypes, create_packet_from}; + +mod packet; + +const KEY: &str = "4eb18d39"; + +const URL: &str = "wss://g2DF33D01-lp1.s.n.srv.nintendo.net"; +#[tokio::main] +async fn main() { + let login = URL.into_client_request().unwrap(); + let (mut stream, response) = tokio_tungstenite::connect_async(login).await.unwrap(); + + println!("response: {:?}", response); + + let packet = create_packet_from( + LiteHeader { + stream_types: StreamTypes::new(10, 10), + source_port: 1, + destination_port: 1, + fragment_id: 0, + types_flags: TypesFlags::default().types(SYN).flags(NEED_ACK), + sequence_id: 0, + ..Default::default() + }, + &[PacketSpecificData::SupportedFunctions(0x8)], + &[], + ); + + println!("sending ack"); + stream.send(Message::Binary(packet.into())).await.unwrap(); + println!("waiting for response"); + let packet = stream.next().await.unwrap(); + let Message::Binary(packet) = packet.unwrap() else { + panic!() + }; + let packet = LitePacket::new(packet); + + let header = packet.header().unwrap(); + + println!("{:?}", header); +} diff --git a/prudplite/src/packet.rs b/prudplite/src/packet.rs new file mode 100644 index 0000000..bd8d87c --- /dev/null +++ b/prudplite/src/packet.rs @@ -0,0 +1,223 @@ +use std::{ + fmt::Debug, + io::{self, Cursor, Read, Write}, +}; + +use bytemuck::{Pod, Zeroable, bytes_of}; +use futures_util::Stream; +use rnex_core::prudp::types_flags::TypesFlags; +use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; + +#[derive(Pod, Zeroable, Copy, Clone, Default, Debug)] +#[repr(C)] +pub struct LiteHeader { + pub magic: u8, + pub packet_specific_length: u8, + pub payload_size: u16, + pub stream_types: StreamTypes, + pub source_port: u8, + pub destination_port: u8, + pub fragment_id: u8, + pub types_flags: TypesFlags, + pub sequence_id: u16, +} + +pub enum PacketSpecificData { + SupportedFunctions(u32), + ConnectionSignature([u8; 16]), + LiteSignature([u8; 16]), +} + +impl PacketSpecificData { + fn consume(reader: &mut impl Read) -> io::Result { + let mut option_id = 0; + reader.read_exact(&mut [option_id])?; + let mut size = 0; + reader.read_exact(&mut [size])?; + + match option_id { + 0 => { + if size != 4 { + Err(io::Error::other( + "invalid option size for supported functions", + )) + } else { + Ok(Self::SupportedFunctions(reader.read_le_u32()?)) + } + } + 1 => { + if size != 16 { + Err(io::Error::other( + "invalid option size for connection signature", + )) + } else { + Ok(Self::ConnectionSignature( + reader.read_struct(IS_BIG_ENDIAN)?, + )) + } + } + 0x80 => { + if size != 16 { + Err(io::Error::other("invalid option size for lite signature")) + } else { + Ok(Self::LiteSignature(reader.read_struct(IS_BIG_ENDIAN)?)) + } + } + _ => Err(io::Error::other("invalid option id")), + } + } + + fn write_size(&self) -> usize { + 2 + match self { + PacketSpecificData::SupportedFunctions(_) => 4, + Self::ConnectionSignature(_) => 16, + Self::LiteSignature(_) => 16, + } + } + + fn write_self(&self, writer: &mut impl Write) -> io::Result<()> { + match self { + PacketSpecificData::SupportedFunctions(v) => { + writer.write_all(&[0, 4])?; + writer.write_all(&v.to_le_bytes())?; + } + Self::ConnectionSignature(v) => { + writer.write_all(&[1, 16])?; + writer.write_all(&v[..])?; + } + Self::LiteSignature(v) => { + writer.write_all(&[0x80, 16])?; + writer.write_all(&v[..])?; + } + } + + Ok(()) + } +} + +pub struct LitePacket>(T); + +pub struct PacketSpecificIter<'a>(Cursor<&'a [u8]>); + +impl<'a> Iterator for PacketSpecificIter<'a> { + type Item = PacketSpecificData; + + fn next(&mut self) -> Option { + PacketSpecificData::consume(&mut self.0).ok() + } +} + +impl> LitePacket { + pub fn new(inner: T) -> Self { + Self(inner) + } + + pub fn header(&self) -> Option<&LiteHeader> { + bytemuck::try_from_bytes(self.0.as_ref().get(..size_of::())?).ok() + } + pub fn header_mut(&mut self) -> Option<&mut LiteHeader> + where + T: AsMut<[u8]>, + { + bytemuck::try_from_bytes_mut(self.0.as_mut().get_mut(..size_of::())?).ok() + } + + pub fn payload(&self) -> Option<&[u8]> { + let header = self.header()?; + self.0 + .as_ref() + .get(size_of::() + header.packet_specific_length as usize..) + } + + pub fn payload_mut(&mut self) -> Option<&mut [u8]> + where + T: AsMut<[u8]>, + { + let len = self.header()?.packet_specific_length; + self.0 + .as_mut() + .get_mut(size_of::() + len as usize..) + } + + pub fn packet_specific_raw(&self) -> Option<&[u8]> { + let header = self.header()?; + self.0.as_ref().get( + size_of::() + ..size_of::() + header.packet_specific_length as usize, + ) + } + pub fn packet_specific_raw_mut(&mut self) -> Option<&mut [u8]> + where + T: AsMut<[u8]>, + { + let len = self.header()?.packet_specific_length; + self.0 + .as_mut() + .get_mut(size_of::()..size_of::() + len as usize) + } + + pub fn packet_specific_iter(&self) -> Option { + self.packet_specific_raw() + .map(Cursor::new) + .map(PacketSpecificIter) + } +} + +pub fn create_packet_from( + header: LiteHeader, + specific_data: &[PacketSpecificData], + data: &[u8], +) -> Vec { + let specific_size: usize = specific_data.iter().map(|v| v.write_size()).sum(); + let mut packet = LitePacket::new(vec![ + 0u8; + size_of::() + specific_size + data.len() + ]); + + *packet.header_mut().expect("packet malformed in creation") = LiteHeader { + magic: 0x80, + packet_specific_length: specific_size as u8, + payload_size: data.len() as u16, + ..header + }; + + let mut cursor = Cursor::new( + packet + .packet_specific_raw_mut() + .expect("packet malformed in creation"), + ); + + for specific in specific_data { + specific.write_self(&mut cursor).unwrap(); + } + + packet + .payload_mut() + .expect("packet malformed in creation") + .copy_from_slice(data); + + packet.0 +} + +#[derive(Pod, Zeroable, Copy, Clone, Default)] +#[repr(transparent)] +pub struct StreamTypes(u8); + +impl Debug for StreamTypes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "({},{})", self.source(), self.destination()) + } +} + +impl StreamTypes { + pub fn new(source_stream: u8, dest_stream: u8) -> Self { + Self((source_stream & 0xF << 4) & dest_stream & 0xF) + } + + pub fn source(&self) -> u8 { + self.0 >> 4 + } + pub fn destination(&self) -> u8 { + self.0 & 0xF + } +} diff --git a/prudpv0/src/server.rs b/prudpv0/src/server.rs index aa603f7..1305d81 100644 --- a/prudpv0/src/server.rs +++ b/prudpv0/src/server.rs @@ -100,7 +100,7 @@ impl Server { }; /* we leave the sequence id as is for now as it defaults to 0 */ - *packet.checksum_mut().expect("packet malformed in creation") = + packet.checksum_mut().expect("packet malformed in creation") = self.crypto.calculate_checksum( packet .checksummed_data() @@ -434,7 +434,7 @@ impl Server { info!("len: {}", packet.0.len()); - let addr = PRUDPSockAddr::new(addr, header.source); + let addr = PRUDPSockAddr::new(SocketAddr::V4(addr), header.source); if let Some(conn) = self.get_connection(addr).await { let mut inner = conn.inner.lock().await; diff --git a/rnex-core/Cargo.toml b/rnex-core/Cargo.toml index 69a4f9f..3a05177 100644 --- a/rnex-core/Cargo.toml +++ b/rnex-core/Cargo.toml @@ -34,6 +34,11 @@ ureq = "3.1.4" rmc_struct_header = [] guest_login = [] friends = ["guest_login"] +big_pid = [] +v3-8-15 = ["rmc_struct_header"] +v4-3-11 = ["v3-8-15"] +nx = ["big_pid"] + [[bench]] name = "rmc_serialization" diff --git a/rnex-core/src/executables/backend_server_secure.rs b/rnex-core/src/executables/backend_server_secure.rs index 51d9647..91b83b3 100644 --- a/rnex-core/src/executables/backend_server_secure.rs +++ b/rnex-core/src/executables/backend_server_secure.rs @@ -17,6 +17,7 @@ async fn main() { if #[cfg(feature = "friends")]{ start_friends_backend().await; } else { + use rnex_core::executables::regular_backend; regular_backend::start_regular_backend().await } } diff --git a/rnex-core/src/executables/friends_backend.rs b/rnex-core/src/executables/friends_backend.rs index 2626fbe..7ea03b4 100644 --- a/rnex-core/src/executables/friends_backend.rs +++ b/rnex-core/src/executables/friends_backend.rs @@ -1,22 +1,65 @@ -use std::sync::{Arc, atomic::AtomicU32}; +use std::{ + io::Cursor, + net::SocketAddrV4, + sync::{Arc, atomic::AtomicU32}, +}; + +use log::error; +use tokio::net::TcpListener; use crate::{ - executables::common::new_simple_backend, - nex::friends_handler::{FriendsManager, FriendsUser}, + executables::common::{OWN_IP_PRIVATE, SERVER_PORT, new_simple_backend}, + nex::friends_handler::{FriendsGuest, FriendsManager, FriendsUser}, + reggie::UnitPacketRead, + rmc::{ + protocols::{RmcCallable, new_rmc_gateway_connection}, + structures::RmcSerialize, + }, + rnex_proxy_common::ConnectionInitData, }; pub async fn start_friends_backend() { let fm = Arc::new(FriendsManager { cid_counter: AtomicU32::new(1), }); + let listen = TcpListener::bind(SocketAddrV4::new(*OWN_IP_PRIVATE, *SERVER_PORT)) + .await + .unwrap(); + while let Ok((mut stream, _addr)) = listen.accept().await { + let buffer = match stream.read_buffer().await { + Ok(v) => v, + Err(e) => { + error!( + "an error ocurred whilest reading connection data buffer: {:?}", + e + ); + continue; + } + }; - new_simple_backend(move |c, r| { + let user_connection_data = ConnectionInitData::deserialize(&mut Cursor::new(buffer)); + + let c = match user_connection_data { + Ok(v) => v, + Err(e) => { + error!("an error ocurred whilest reading connection data: {:?}", e); + continue; + } + }; let fm = fm.clone(); - Arc::new_cyclic(move |this| FriendsUser { - fm, - addr: c.prudpsock_addr, - pid: c.pid, - }) - }) - .await; + if c.pid != 100 { + new_rmc_gateway_connection(stream.into(), move |r| { + Arc::new_cyclic(move |this| FriendsUser { + fm, + addr: c.prudpsock_addr, + pid: c.pid, + }) + }); + } else { + Arc::new_cyclic(move |this| FriendsGuest { + fm, + addr: c.prudpsock_addr, + }); + } + } } diff --git a/rnex-core/src/grpc/account.rs b/rnex-core/src/grpc/account.rs index a70149f..64be4e9 100644 --- a/rnex-core/src/grpc/account.rs +++ b/rnex-core/src/grpc/account.rs @@ -1,29 +1,27 @@ -use std::{env, result}; +use crate::grpc::account::Error::SomethingHappened; +use json::{JsonValue, object}; +use once_cell::sync::Lazy; +use rnex_core::PID; use std::array::TryFromSliceError; use std::ops::Deref; -use json::{object, JsonValue}; -use once_cell::sync::Lazy; +use std::{env, result}; use thiserror::Error; -use tokio::task::{spawn_blocking, JoinError}; -use crate::grpc::account::Error::SomethingHappened; -static API_KEY: Lazy = Lazy::new(||{ - let key = env::var("ACCOUNT_GQL_API_KEY") - .expect("no graphql ip specified"); +use tokio::task::{JoinError, spawn_blocking}; +static API_KEY: Lazy = Lazy::new(|| { + let key = env::var("ACCOUNT_GQL_API_KEY").expect("no graphql ip specified"); key }); -static CLIENT_URI: Lazy = Lazy::new(||{ +static CLIENT_URI: Lazy = Lazy::new(|| { env::var("ACCOUNT_GQL_URL") .ok() .and_then(|s| s.parse().ok()) .expect("no graphql ip specified") }); - - #[derive(Error, Debug)] -pub enum Error{ +pub enum Error { #[error(transparent)] RequestError(#[from] ureq::Error), #[error(transparent)] @@ -35,20 +33,20 @@ pub enum Error{ #[error("something happened")] SomethingHappened, #[error("error joining blocking task: {0}")] - Join(#[from] JoinError) + Join(#[from] JoinError), } pub type Result = result::Result; -pub struct Client;//(reqwest::Client); +pub struct Client; //(reqwest::Client); -impl Client{ +impl Client { pub async fn new() -> Result { //Ok(Self(reqwest::ClientBuilder::new().build()?)) Ok(Self) } - async fn do_request(&self, request_data: JsonValue) -> Result{ + async fn do_request(&self, request_data: JsonValue) -> Result { let request = ureq::post(CLIENT_URI.as_str()) .header("X-API-Key", API_KEY.deref()) .content_type("application/json"); @@ -70,28 +68,35 @@ impl Client{ */ } - pub async fn get_nex_password(&mut self , pid: u32) -> Result<[u8; 16]>{ - let req = self.do_request(object!{ - "query": r"query($pid: Int!){ + pub async fn get_nex_password(&mut self, pid: PID) -> Result<[u8; 16]> { + let req = self + .do_request(object! { + "query": r"query($pid: Int!){ userByPid(pid: $pid){ nexPassword } }", - "variables": { - "pid": pid - } - }).await?; + "variables": { + "pid": pid + } + }) + .await?; - let Some(val) = req.entries() + let Some(val) = req + .entries() .find(|v| v.0 == "data") - .ok_or(SomethingHappened)?.1 + .ok_or(SomethingHappened)? + .1 .entries() .find(|v| v.0 == "userByPid") - .ok_or(SomethingHappened)?.1 + .ok_or(SomethingHappened)? + .1 .entries() .find(|v| v.0 == "nexPassword") - .ok_or(SomethingHappened)?.1 - .as_str() else { + .ok_or(SomethingHappened)? + .1 + .as_str() + else { return Err(SomethingHappened); }; @@ -109,8 +114,6 @@ impl Client{ }*/ } - - /* pub struct Client(AccountClient>); @@ -148,4 +151,4 @@ impl Client{ Ok(response) } } -*/ \ No newline at end of file +*/ diff --git a/rnex-core/src/kerberos/mod.rs b/rnex-core/src/kerberos/mod.rs index 4b56f56..d02afa1 100644 --- a/rnex-core/src/kerberos/mod.rs +++ b/rnex-core/src/kerberos/mod.rs @@ -6,18 +6,21 @@ use hmac::Mac; use md5::{Digest, Md5}; use rc4::KeyInit; use rc4::cipher::StreamCipherCoreWrapper; -use rc4::consts::U16; use rc4::{Rc4, Rc4Core, StreamCipher}; use rnex_core::rmc::structures::RmcSerialize; use std::io::{Read, Write}; +use typenum::U16; use typenum::Unsigned; use rnex_core::rmc::structures::Result; +use rnex_core::PID; + cfg_if! { if #[cfg(feature = "friends")]{ pub type SESSION_KEY_LENGTH_TY = U16; } else { + use rc4::consts::U32; pub type SESSION_KEY_LENGTH_TY = U32; } } @@ -25,7 +28,7 @@ pub const SESSION_KEY_LENGTH: usize = SESSION_KEY_LENGTH_TY::USIZE; type Md5Hmac = Hmac; -pub fn derive_key(pid: u32, password: &[u8]) -> [u8; 16] { +pub fn derive_key(pid: PID, password: &[u8]) -> [u8; 16] { let iteration_count = 65000 + pid % 1024; // we do one iteration out here to ensure the key is always 16 bytes @@ -125,12 +128,12 @@ impl RmcSerialize for KerberosDateTime { #[repr(C, packed)] pub struct TicketInternalData { pub issued_time: KerberosDateTime, - pub pid: u32, + pub pid: PID, pub session_key: [u8; SESSION_KEY_LENGTH], } impl TicketInternalData { - pub(crate) fn new(pid: u32) -> Self { + pub(crate) fn new(pid: PID) -> Self { Self { issued_time: KerberosDateTime::now(), pid, @@ -162,7 +165,7 @@ impl TicketInternalData { #[repr(C, packed)] pub struct Ticket { pub session_key: [u8; SESSION_KEY_LENGTH], - pub pid: u32, + pub pid: PID, } impl Ticket { diff --git a/rnex-core/src/lib.rs b/rnex-core/src/lib.rs index 3920565..28bdd48 100644 --- a/rnex-core/src/lib.rs +++ b/rnex-core/src/lib.rs @@ -4,7 +4,10 @@ #![allow(async_fn_in_trait)] //#![warn(missing_docs)] - +#[cfg(feature = "big_pid")] +pub type PID = u64; +#[cfg(not(feature = "big_pid"))] +pub type PID = u32; extern crate self as rnex_core; @@ -12,18 +15,18 @@ pub mod prudp; pub mod rmc; //mod protocols; +pub mod common; +pub mod executables; pub mod grpc; pub mod kerberos; pub mod nex; -pub mod result; -pub mod versions; -pub mod common; pub mod reggie; +pub mod result; pub mod rnex_proxy_common; pub mod util; -pub mod executables; +pub mod versions; pub use macros::*; -pub mod config{ +pub mod config { pub const FEATURE_HAS_STRUCT_HEADER: bool = cfg!(feature = "rmc_struct_header"); } diff --git a/rnex-core/src/nex/account.rs b/rnex-core/src/nex/account.rs index b3a5bb6..eb542ff 100644 --- a/rnex-core/src/nex/account.rs +++ b/rnex-core/src/nex/account.rs @@ -1,16 +1,16 @@ use macros::RmcSerialize; +use rnex_core::PID; + #[derive(RmcSerialize, Clone)] pub struct Account { - pub pid: u32, + pub pid: PID, pub username: String, pub kerbros_password: Box<[u8]>, } impl Account { - pub fn new(pid: u32, username: &str, passwd: &str) -> Self { - let passwd_data = passwd.as_bytes(); - + pub fn new(pid: PID, username: &str, passwd: &str) -> Self { Self { kerbros_password: passwd.as_bytes().into(), username: username.into(), @@ -18,7 +18,7 @@ impl Account { } } - pub fn new_raw_password(pid: u32, username: &str, passwd: &[u8]) -> Self { + pub fn new_raw_password(pid: PID, username: &str, passwd: &[u8]) -> Self { Self { kerbros_password: passwd.into(), username: username.into(), @@ -26,7 +26,7 @@ impl Account { } } - pub fn get_login_data(&self) -> (u32, &[u8]) { + pub fn get_login_data(&self) -> (PID, &[u8]) { (self.pid, &self.kerbros_password) } } diff --git a/rnex-core/src/nex/auth_handler.rs b/rnex-core/src/nex/auth_handler.rs index 0ab6910..4d5392d 100644 --- a/rnex-core/src/nex/auth_handler.rs +++ b/rnex-core/src/nex/auth_handler.rs @@ -4,6 +4,7 @@ use crate::{define_rmc_proto, kerberos}; use cfg_if::cfg_if; use log::{info, warn}; use macros::rmc_struct; +use rnex_core::PID; use rnex_core::kerberos::{KerberosDateTime, Ticket, derive_key}; use rnex_core::nex::account::Account; use rnex_core::rmc::protocols::OnlyRemote; @@ -16,7 +17,7 @@ use rnex_core::rmc::structures::connection_data::ConnectionDataOld; use rnex_core::rmc::structures::qresult::QResult; use std::hash::{DefaultHasher, Hasher}; use std::net::SocketAddrV4; -use std::sync::{Arc, LazyLock, OnceLock}; +use std::sync::{Arc, LazyLock}; define_rmc_proto!( proto AuthClientProtocol{ @@ -33,8 +34,8 @@ pub struct AuthHandler { } pub fn generate_ticket( - source_act_login_data: (u32, &[u8]), - dest_act_login_data: (u32, &[u8]), + source_act_login_data: (PID, &[u8]), + dest_act_login_data: (PID, &[u8]), ) -> Box<[u8]> { let source_key = derive_key(source_act_login_data.0, source_act_login_data.1); let dest_key = derive_key(dest_act_login_data.0, dest_act_login_data.1); @@ -50,8 +51,28 @@ pub fn generate_ticket( encrypted_session_ticket } +pub fn generate_ticket_with_string_user_key( + source_act: PID, + dest_act_login_data: (PID, &[u8]), +) -> (String, Box<[u8]>) { + let source_key: [u8; 8] = rand::random(); + let key_string = hex::encode(source_key); + let key_data: [u8; 16] = key_string.as_bytes().try_into().unwrap(); + let dest_key = derive_key(dest_act_login_data.0, dest_act_login_data.1); -async fn get_login_data_by_pid(pid: u32) -> Option<(u32, Box<[u8]>)> { + let internal_data = kerberos::TicketInternalData::new(source_act); + + let encrypted_inner = internal_data.encrypt(dest_key); + let encrypted_session_ticket = Ticket { + pid: dest_act_login_data.0, + session_key: internal_data.session_key, + } + .encrypt(key_data, &encrypted_inner); + + (key_string, encrypted_session_ticket) +} + +async fn get_login_data_by_pid(pid: PID) -> Option<(PID, Box<[u8]>)> { if pid == GUEST_ACCOUNT.pid { let source_login_data = GUEST_ACCOUNT.get_login_data(); @@ -84,7 +105,7 @@ impl AuthHandler { pub async fn generate_ticket_from_name( &self, name: &str, - ) -> Result<(u32, Box<[u8]>), ErrorCode> { + ) -> Result<(PID, Box<[u8]>), ErrorCode> { #[cfg(feature = "guest_login")] { if name == GUEST_ACCOUNT.username { @@ -120,13 +141,39 @@ impl AuthHandler { generate_ticket(source_login_data, destination_login_data), )) } + + pub async fn generate_ticket_from_name_string_user_key( + &self, + name: &str, + ) -> Result<(PID, String, Box<[u8]>), ErrorCode> { + #[cfg(feature = "guest_login")] + { + if name == GUEST_ACCOUNT.username { + let source_login_data = GUEST_ACCOUNT.get_login_data(); + let destination_login_data = self.destination_server_acct.get_login_data(); + + return Ok(( + source_login_data.0, + generate_ticket(source_login_data, destination_login_data), + )); + } + } + let Ok(pid) = name.parse() else { + warn!("unable to connect to parse pid: {}", name); + return Err(ErrorCode::Core_InvalidArgument); + }; + let destination_login_data = self.destination_server_acct.get_login_data(); + + let data = generate_ticket_with_string_user_key(pid, destination_login_data); + Ok((pid, data.0, data.1)) + } } impl Auth for AuthHandler { async fn login( &self, name: String, - ) -> Result<(QResult, u32, Vec, ConnectionDataOld, String), ErrorCode> { + ) -> Result<(QResult, PID, Vec, ConnectionDataOld, String), ErrorCode> { let (pid, ticket) = self.generate_ticket_from_name(&name).await?; let result = QResult::success(Core_Unknown); @@ -157,103 +204,135 @@ impl Auth for AuthHandler { info!("data: {:?}", ret); Ok(ret) } + cfg_if! { - async fn login_ex( - &self, - name: String, - _extra_data: Any, - ) -> Result<(QResult, u32, Vec, ConnectionData, String), ErrorCode> { - let (pid, ticket) = self.generate_ticket_from_name(&name).await?; + if #[cfg(feature = "nx")]{ + async fn login_ex( + &self, + name: String, + _extra_data: Any, + ) -> Result<(QResult, PID, Vec, ConnectionData, String, String), ErrorCode> { + let (pid, key, ticket) = self.generate_ticket_from_name_string_user_key(&name).await?; - let result = QResult::success(Core_Unknown); + let result = QResult::success(Core_Unknown); - let mut hasher = DefaultHasher::new(); + let mut hasher = DefaultHasher::new(); - hasher.write(name.as_bytes()); + hasher.write(name.as_bytes()); - let Ok(addr) = self.control_server.get_url(hasher.finish()).await else { - warn!("no secure proxies"); - return Err(ErrorCode::Core_Exception); - }; + let Ok(addr) = self.control_server.get_url(hasher.finish()).await else { + warn!("no secure proxies"); + return Err(ErrorCode::Core_Exception); + }; - let connection_data = ConnectionData { - station_url: station_url_from_sock_addr(addr), - special_station_url: "".to_string(), - //date_time: KerberosDateTime::new(1,1,1,1,1,1), - date_time: KerberosDateTime::now(), - special_protocols: Vec::new(), - }; + let connection_data = ConnectionData { + station_url: station_url_from_sock_addr(addr), + special_station_url: "".to_string(), + //date_time: KerberosDateTime::new(1,1,1,1,1,1), + date_time: KerberosDateTime::now(), + special_protocols: Vec::new(), + }; - let ret = ( - result, - pid, - ticket.into(), - connection_data, - self.build_name.to_string(), - ); + let ret = ( + result, + pid, + ticket.into(), + connection_data, + self.build_name.to_string(), + key + ); - info!("data: {:?}", ret); - Ok(ret) - } + info!("data: {:?}", ret); + Ok(ret) + } + async fn request_ticket( + &self, + source_pid: PID, + destination_pid: PID, + ) -> Result<(QResult, Vec, String), ErrorCode> { + let Some((pid, _)) = get_login_data_by_pid(source_pid).await else { + return Err(ErrorCode::Core_Exception); + }; - async fn request_ticket( - &self, - source_pid: u32, - destination_pid: u32, - ) -> Result<(QResult, Vec), ErrorCode> { - let Some((pid, passwd)) = get_login_data_by_pid(source_pid).await else { - return Err(ErrorCode::Core_Exception); - }; + let desgination_login_data = if destination_pid == self.destination_server_acct.pid { + self.destination_server_acct.get_login_data() + } else { + return Err(ErrorCode::RendezVous_InvalidOperation); + }; - let desgination_login_data = if destination_pid == self.destination_server_acct.pid { - self.destination_server_acct.get_login_data() + let result = QResult::success(Core_Unknown); + + let ticket = generate_ticket_with_string_user_key(pid, desgination_login_data); + + Ok((result, ticket.1.into(), ticket.0)) + } } else { - return Err(ErrorCode::RendezVous_InvalidOperation); - }; + async fn login_ex( + &self, + name: String, + _extra_data: Any, + ) -> Result<(QResult, PID, Vec, ConnectionData, String), ErrorCode> { + let (pid, ticket) = self.generate_ticket_from_name(&name).await?; - let result = QResult::success(Core_Unknown); + let result = QResult::success(Core_Unknown); - let ticket = generate_ticket((pid, &passwd[..]), desgination_login_data); + let mut hasher = DefaultHasher::new(); - Ok((result, ticket.into())) + hasher.write(name.as_bytes()); + + let Ok(addr) = self.control_server.get_url(hasher.finish()).await else { + warn!("no secure proxies"); + return Err(ErrorCode::Core_Exception); + }; + + let connection_data = ConnectionData { + station_url: station_url_from_sock_addr(addr), + special_station_url: "".to_string(), + //date_time: KerberosDateTime::new(1,1,1,1,1,1), + date_time: KerberosDateTime::now(), + special_protocols: Vec::new(), + }; + + let ret = ( + result, + pid, + ticket.into(), + connection_data, + self.build_name.to_string(), + ); + + info!("data: {:?}", ret); + Ok(ret) + } + async fn request_ticket( + &self, + source_pid: PID, + destination_pid: PID, + ) -> Result<(QResult, Vec), ErrorCode> { + let Some((pid, passwd)) = get_login_data_by_pid(source_pid).await else { + return Err(ErrorCode::Core_Exception); + }; + + let desgination_login_data = if destination_pid == self.destination_server_acct.pid { + self.destination_server_acct.get_login_data() + } else { + return Err(ErrorCode::RendezVous_InvalidOperation); + }; + + let result = QResult::success(Core_Unknown); + + let ticket = generate_ticket((pid, &passwd[..]), desgination_login_data); + + Ok((result, ticket.into())) + } + } } async fn get_pid(&self, _username: String) -> Result { Err(ErrorCode::Core_Exception) } - async fn get_name(&self, _pid: u32) -> Result { + async fn get_name(&self, _pid: PID) -> Result { Err(ErrorCode::Core_Exception) } } - -#[cfg(test)] -mod test { - use rnex_core::rmc::response::RMCResponse; - use rnex_core::rmc::structures::RmcSerialize; - use rnex_core::rmc::structures::connection_data::ConnectionData; - use rnex_core::rmc::structures::qresult::QResult; - use std::io::Cursor; - - #[test] - fn test() { - return; - let stuff = hex::decode("200100000a0106000000028000000100010051b3995774000000a6321c7f78847c1c5e9fb825eb26bd91841f1a40d92fc694159666119cb13527f1463ac48ad42a63e6613ede67041554b1770978112e6f1f3e177a2bfc75933216dbe38f70133a1eb28e2ae32a4b5c4b0c3e3efd4c02907992e259b257270b57a9dbe7792f4721b07f8fafb9e32d50f2555c616a015c0000004b007072756470733a2f5049443d323b7369643d313b73747265616d3d31303b747970653d323b616464726573733d322e3234332e39352e3131333b706f72743d31303030313b4349443d3100000000000100002c153ba51f00000033006272616e63683a6f726967696e2f70726f6a6563742f7775702d61676d6a206275696c643a335f385f31355f323030345f3000").unwrap(); - let stuff = RMCResponse::new(&mut Cursor::new(stuff)).unwrap(); - - let rnex_core::rmc::response::RMCResponseResult::Success { .. } = 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/rnex-core/src/nex/common.rs b/rnex-core/src/nex/common.rs index d2fa40f..48b080d 100644 --- a/rnex-core/src/nex/common.rs +++ b/rnex-core/src/nex/common.rs @@ -8,10 +8,12 @@ use rnex_core::rmc::response::ErrorCode::Core_Exception; use rnex_core::prudp::socket_addr::PRUDPSockAddr; use rnex_core::rmc::response::ErrorCode; +use rnex_core::PID; + pub async fn get_station_urls( station_urls: &[StationUrl], addr: PRUDPSockAddr, - pid: u32, + pid: PID, cid: u32, ) -> Result, ErrorCode> { let mut public_station: Option = None; @@ -66,7 +68,7 @@ pub async fn get_station_urls( public_station .options - .push(Address(*addr.regular_socket_addr.ip())); + .push(Address(addr.regular_socket_addr.ip().clone())); public_station .options .push(Port(addr.regular_socket_addr.port())); diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index a2d42ca..be5d684 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -1,5 +1,4 @@ use std::sync::{Arc, atomic::AtomicU32}; -use std::time::Duration; use log::info; use macros::rmc_struct; @@ -20,21 +19,33 @@ use rnex_core::{ }, }; use std::sync::atomic::Ordering::Relaxed; -use tokio::time::sleep; use rnex_core::rmc::protocols::friends::{GameKey, MiiV2, PrincipalBasicInfo}; +use rnex_core::PID; + define_rmc_proto!( proto FriendsUser{ Secure, Friends } ); +define_rmc_proto!( + proto FriendsGuest{ + Secure + } +); #[rmc_struct(FriendsUser)] pub struct FriendsUser { pub fm: Arc, pub addr: PRUDPSockAddr, - pub pid: u32, + pub pid: PID, +} + +#[rmc_struct(FriendsGuest)] +pub struct FriendsGuest { + pub fm: Arc, + pub addr: PRUDPSockAddr, } pub struct FriendsManager { @@ -50,9 +61,9 @@ impl FriendsManager { impl Friends for FriendsUser { async fn update_and_get_all_information( &self, - info: NNAInfo, - presence: NintendoPresenceV2, - date_time: KerberosDateTime, + _info: NNAInfo, + _presence: NintendoPresenceV2, + _date_time: KerberosDateTime, ) -> Result< ( PrincipalPreference, @@ -89,10 +100,10 @@ impl Friends for FriendsUser { nna_info: NNAInfo { principal_basic_info: PrincipalBasicInfo { pid: 101, - nnid: "dummyaccount".to_string(), + nnid: "dummy:3".to_string(), mii: MiiV2{ date_time: KerberosDateTime::now(), - name: "Dummy Account".to_string(), + name: "TheDummy".to_string(), mii_data: hex::decode("030000402bd7c32986a771f2dc6b35e31da15e37ff7c0000391e6f006f006d0069000000000000000000000000004040001065033568641e2013661a611821640f0000290052485000000000000000000000000000000000000000000000e838").unwrap(), unk: 0, unk2: 0, @@ -149,12 +160,37 @@ impl Secure for FriendsUser { async fn register_ex( &self, station_urls: Vec, - data: Any, + _data: Any, ) -> Result<(QResult, u32, StationUrl), ErrorCode> { info!("register"); self.register(station_urls).await } - async fn replace_url(&self, target: StationUrl, dest: StationUrl) -> Result<(), ErrorCode> { + async fn replace_url(&self, _target: StationUrl, _dest: StationUrl) -> Result<(), ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } +} + +impl Secure for FriendsGuest { + async fn register( + &self, + station_urls: Vec, + ) -> Result<(QResult, u32, StationUrl), ErrorCode> { + let cid = self.fm.next_cid(); + Ok(( + QResult::success(ErrorCode::Core_Unknown), + cid, + get_station_urls(&station_urls, self.addr, 100, cid).await?[0].clone(), + )) + } + async fn register_ex( + &self, + station_urls: Vec, + _data: Any, + ) -> Result<(QResult, u32, StationUrl), ErrorCode> { + info!("register"); + self.register(station_urls).await + } + async fn replace_url(&self, _target: StationUrl, _dest: StationUrl) -> Result<(), ErrorCode> { Err(ErrorCode::Core_NotImplemented) } } diff --git a/rnex-core/src/nex/matchmake.rs b/rnex-core/src/nex/matchmake.rs index 732c1ea..0a155fa 100644 --- a/rnex-core/src/nex/matchmake.rs +++ b/rnex-core/src/nex/matchmake.rs @@ -2,6 +2,7 @@ use crate::nex::user::User; use crate::rmc::protocols::notifications::{NotificationEvent, RemoteNotification}; use log::info; use rand::random; +use rnex_core::PID; use rnex_core::kerberos::KerberosDateTime; use rnex_core::rmc::protocols::notifications::notification_types::{ HOST_CHANGED, OWNERSHIP_CHANGED, @@ -208,7 +209,7 @@ impl ExtendedMatchmakeSession { .process_notification_event(NotificationEvent { pid_source: initiating_pid, notif_type: 122000, - param_1: self.session.gathering.self_gid, + param_1: self.session.gathering.self_gid as PID, param_2: other_pid, str_param: "".into(), param_3: 0, @@ -240,7 +241,7 @@ impl ExtendedMatchmakeSession { .process_notification_event(NotificationEvent { pid_source: initiating_pid, notif_type: 3001, - param_1: self.session.gathering.self_gid, + param_1: self.session.gathering.self_gid as PID, param_2: *pid, str_param: join_msg.clone(), param_3: self.connected_players.len() as _, @@ -261,7 +262,7 @@ impl ExtendedMatchmakeSession { .process_notification_event(NotificationEvent { pid_source: initiating_pid, notif_type: 3001, - param_1: self.session.gathering.self_gid, + param_1: self.session.gathering.self_gid as PID, param_2: older_pid, str_param: join_msg.clone(), param_3: self.connected_players.len() as _, @@ -393,7 +394,7 @@ impl ExtendedMatchmakeSession { Ok(true) } - pub async fn migrate_ownership(&mut self, initiator_pid: u32) -> Result<(), ErrorCode> { + pub async fn migrate_ownership(&mut self, initiator_pid: PID) -> Result<(), ErrorCode> { let players: Vec<_> = self .connected_players .iter() @@ -414,7 +415,7 @@ impl ExtendedMatchmakeSession { self.broadcast_notification(&NotificationEvent { pid_source: initiator_pid, notif_type: OWNERSHIP_CHANGED, - param_1: self.session.gathering.self_gid, + param_1: self.session.gathering.self_gid as PID, param_2: new_owner.pid, ..Default::default() }) @@ -423,7 +424,7 @@ impl ExtendedMatchmakeSession { Ok(()) } - pub async fn migrate_host(&mut self, initiator_pid: u32) -> Result<(), ErrorCode> { + pub async fn migrate_host(&mut self, initiator_pid: PID) -> Result<(), ErrorCode> { // let players: Vec<_> = self.connected_players.iter().filter_map(|p| p.upgrade()).collect(); self.session.gathering.host_pid = self.session.gathering.owner_pid; @@ -431,7 +432,7 @@ impl ExtendedMatchmakeSession { self.broadcast_notification(&NotificationEvent { pid_source: initiator_pid, notif_type: HOST_CHANGED, - param_1: self.session.gathering.self_gid, + param_1: self.session.gathering.self_gid as PID, ..Default::default() }) .await; @@ -441,7 +442,7 @@ impl ExtendedMatchmakeSession { pub async fn remove_player_from_session( &mut self, - pid: u32, + pid: PID, message: &str, ) -> Result<(), ErrorCode> { self.connected_players @@ -468,7 +469,7 @@ impl ExtendedMatchmakeSession { .process_notification_event(NotificationEvent { notif_type: 3008, pid_source: pid, - param_1: self.session.gathering.self_gid, + param_1: self.session.gathering.self_gid as PID, param_2: pid, str_param: message.to_owned(), ..Default::default() diff --git a/rnex-core/src/nex/user.rs b/rnex-core/src/nex/user.rs index 1666d56..d8450e9 100644 --- a/rnex-core/src/nex/user.rs +++ b/rnex-core/src/nex/user.rs @@ -9,9 +9,10 @@ use crate::rmc::protocols::nat_traversal::{ NatTraversal, RawNatTraversal, RawNatTraversalInfo, RemoteNatTraversal, RemoteNatTraversalConsole, }; +use rnex_core::PID; use rnex_core::prudp::station_url::StationUrl; use rnex_core::prudp::station_url::UrlOptions::{ - Address, NatFiltering, NatMapping, NatType, Port, PrincipalID, RVConnectionID, + Address, NatFiltering, NatMapping, Port, RVConnectionID, }; use rnex_core::rmc::protocols::matchmake_ext::{ MatchmakeExt, RawMatchmakeExt, RawMatchmakeExtInfo, RemoteMatchmakeExt, @@ -31,10 +32,7 @@ use crate::rmc::protocols::notifications::{NotificationEvent, RemoteNotification use log::info; use macros::rmc_struct; use rnex_core::prudp::socket_addr::PRUDPSockAddr; -use rnex_core::prudp::station_url::nat_types::PUBLIC; -use rnex_core::rmc::response::ErrorCode::{ - Core_Exception, Core_InvalidArgument, RendezVous_AccountExpired, -}; +use rnex_core::rmc::response::ErrorCode::{Core_InvalidArgument, RendezVous_AccountExpired}; use rnex_core::rmc::structures::qresult::QResult; use std::sync::{Arc, Weak}; use tokio::sync::{Mutex, RwLock}; @@ -52,7 +50,7 @@ define_rmc_proto!( #[rmc_struct(UserProtocol)] pub struct User { - pub pid: u32, + pub pid: PID, pub ip: PRUDPSockAddr, pub this: Weak, pub remote: RemoteConsole, @@ -423,7 +421,7 @@ impl Matchmake for User { .process_notification_event(NotificationEvent { notif_type: 110000, pid_source: self.pid, - param_1: gid, + param_1: gid as PID, param_2: self.pid, param_3: 0, str_param: "".to_string(), @@ -444,7 +442,7 @@ impl Matchmake for User { .process_notification_event(NotificationEvent { notif_type: 4000, pid_source: self.pid, - param_1: gid, + param_1: gid as PID, param_2: self.pid, param_3: 0, str_param: "".to_string(), @@ -459,7 +457,7 @@ impl Matchmake for User { async fn migrate_gathering_ownership( &self, gid: u32, - candidates: Vec, + candidates: Vec, _participants_only: bool, ) -> Result<(), ErrorCode> { let session = self.matchmake_manager.get_session(gid).await?; @@ -479,8 +477,8 @@ impl Matchmake for User { .process_notification_event(NotificationEvent { notif_type: 4000, pid_source: self.pid, - param_1: gid, - param_2: *candidate, + param_1: gid as PID, + param_2: *candidate as PID, param_3: 0, str_param: "".to_string(), }) diff --git a/rnex-core/src/prudp/socket_addr.rs b/rnex-core/src/prudp/socket_addr.rs index 4ffac83..e7cdf94 100644 --- a/rnex-core/src/prudp/socket_addr.rs +++ b/rnex-core/src/prudp/socket_addr.rs @@ -3,19 +3,19 @@ use macros::RmcSerialize; use md5::digest::Mac; use rnex_core::prudp::virtual_port::VirtualPort; use std::io::Write; -use std::net::SocketAddrV4; +use std::net::{IpAddr, SocketAddr}; type Md5Hmac = Hmac; #[derive(Eq, PartialEq, Hash, Debug, Copy, Clone, Ord, PartialOrd, RmcSerialize)] #[rmc_struct(0)] pub struct PRUDPSockAddr { - pub regular_socket_addr: SocketAddrV4, + pub regular_socket_addr: SocketAddr, pub virtual_port: VirtualPort, } impl PRUDPSockAddr { - pub fn new(regular_socket_addr: SocketAddrV4, virtual_port: VirtualPort) -> Self { + pub fn new(regular_socket_addr: SocketAddr, virtual_port: VirtualPort) -> Self { Self { regular_socket_addr, virtual_port, @@ -23,9 +23,12 @@ impl PRUDPSockAddr { } pub fn calculate_connection_signature(&self) -> [u8; 16] { - let mut hmac = Md5Hmac::new_from_slice(&[0; 16]).expect("fuck"); + let mut hmac = Md5Hmac::new_from_slice(&[0; 16]).expect("?"); - let data = self.regular_socket_addr.ip().octets().to_vec(); + let data = match self.regular_socket_addr.ip() { + IpAddr::V4(v) => v.octets().to_vec(), + IpAddr::V6(v) => v.octets().to_vec(), + }; //data.extend_from_slice(&self.regular_socket_addr.port().to_be_bytes()); hmac.write_all(&data) diff --git a/rnex-core/src/prudp/station_url.rs b/rnex-core/src/prudp/station_url.rs index a4665c2..e2d4abf 100644 --- a/rnex-core/src/prudp/station_url.rs +++ b/rnex-core/src/prudp/station_url.rs @@ -1,32 +1,35 @@ -use std::net::Ipv4Addr; +use crate::prudp::station_url::Type::{PRUDP, PRUDPS, UDP}; +use crate::prudp::station_url::UrlOptions::{ + Address, ConnectionID, NatFiltering, NatMapping, NatType, PID, PMP, Platform, Port, + PrincipalID, RVConnectionID, StreamID, StreamType, UPNP, +}; +use crate::rmc::structures::Error::StationUrlInvalid; +use crate::rmc::structures::RmcSerialize; +use crate::rmc::structures::helpers::DummyFormatWriter; use log::error; use std::fmt::{Debug, Display, Formatter, Write}; use std::io::Read; -use crate::prudp::station_url::Type::{PRUDP, PRUDPS, UDP}; -use crate::prudp::station_url::UrlOptions::{Address, ConnectionID, NatFiltering, NatMapping, NatType, Platform, PMP, Port, PrincipalID, RVConnectionID, StreamID, StreamType, UPNP, PID}; -use crate::rmc::structures::Error::StationUrlInvalid; -use crate::rmc::structures::helpers::DummyFormatWriter; -use crate::rmc::structures::RmcSerialize; +use std::net::IpAddr; #[derive(Clone, Copy, PartialEq, Eq)] -pub enum Type{ +pub enum Type { UDP, PRUDP, - PRUDPS + PRUDPS, } -pub mod nat_types{ +pub mod nat_types { pub const BEHIND_NAT: u8 = 1; pub const PUBLIC: u8 = 2; } #[derive(Clone, Eq, PartialEq)] pub enum UrlOptions { - Address(Ipv4Addr), + Address(IpAddr), Port(u16), StreamType(u8), StreamID(u8), ConnectionID(u8), - PrincipalID(u32), + PrincipalID(rnex_core::PID), NatType(u8), NatMapping(u8), NatFiltering(u8), @@ -35,82 +38,52 @@ pub enum UrlOptions { Platform(u8), PMP(u8), PID(u32), - } #[derive(Clone, PartialEq, Eq)] -pub struct StationUrl{ +pub struct StationUrl { pub url_type: Type, - pub options: Vec + pub options: Vec, } -impl StationUrl{ - pub fn read_options(options: &str) -> Option>{ +impl StationUrl { + pub fn read_options(options: &str) -> Option> { let mut options_out = Vec::new(); - for option in options.split(';'){ - if option == "" { continue; } + for option in options.split(';') { + if option == "" { + continue; + } let mut option_parts = option.split('='); - let option_name= option_parts.next()?.to_ascii_lowercase(); + let option_name = option_parts.next()?.to_ascii_lowercase(); let option_value = option_parts.next()?; - match option_name.as_ref(){ - "address" => { - options_out.push(Address(option_value.parse().ok()?)) - }, - "port" => { - options_out.push(Port(option_value.parse().ok()?)) - } - "natf" => { - options_out.push(NatFiltering(option_value.parse().ok()?)) - } - "natm" => { - options_out.push(NatMapping(option_value.parse().ok()?)) - } - "sid" => { - options_out.push(StreamID(option_value.parse().ok()?)) - } - "upnp" => { - options_out.push(UPNP(option_value.parse().ok()?)) - } - "type" => { - options_out.push(NatType(option_value.parse().ok()?)) - } - "stream" => { - options_out.push(StreamType(option_value.parse().ok()?)) - } - "RVCID" => { - options_out.push(RVConnectionID(option_value.parse().ok()?)) - } - "rvcid" => { - options_out.push(RVConnectionID(option_value.parse().ok()?)) - } - "pl" => { - options_out.push(Platform(option_value.parse().ok()?)) - } - "pmp" => { - options_out.push(PMP(option_value.parse().ok()?)) - }, - "pid" => { - options_out.push(PID(option_value.parse().ok()?)) - }, - "PID" => { - options_out.push(PID(option_value.parse().ok()?)) - }, + match option_name.as_ref() { + "address" => options_out.push(Address(option_value.parse().ok()?)), + "port" => options_out.push(Port(option_value.parse().ok()?)), + "natf" => options_out.push(NatFiltering(option_value.parse().ok()?)), + "natm" => options_out.push(NatMapping(option_value.parse().ok()?)), + "sid" => options_out.push(StreamID(option_value.parse().ok()?)), + "upnp" => options_out.push(UPNP(option_value.parse().ok()?)), + "type" => options_out.push(NatType(option_value.parse().ok()?)), + "stream" => options_out.push(StreamType(option_value.parse().ok()?)), + "RVCID" => options_out.push(RVConnectionID(option_value.parse().ok()?)), + "rvcid" => options_out.push(RVConnectionID(option_value.parse().ok()?)), + "pl" => options_out.push(Platform(option_value.parse().ok()?)), + "pmp" => options_out.push(PMP(option_value.parse().ok()?)), + "pid" => options_out.push(PID(option_value.parse().ok()?)), + "PID" => options_out.push(PID(option_value.parse().ok()?)), _ => { error!("unimplemented option type, skipping: {}", option_name); } } - - - } Some(options_out) } } -impl TryFrom<&str> for StationUrl{ +impl TryFrom<&str> for StationUrl { type Error = (); fn try_from(value: &str) -> Result { @@ -118,35 +91,30 @@ impl TryFrom<&str> for StationUrl{ let options = &options[2..]; - let url_type = match url_type{ + let url_type = match url_type { "udp" => UDP, "prudp" => PRUDP, "prudps" => PRUDPS, - _ => return Err(()) + _ => return Err(()), }; let options = Self::read_options(options).ok_or(())?; - Ok( - Self{ - url_type, - options - } - ) + Ok(Self { url_type, options }) } } -impl Display for StationUrl{ +impl Display for StationUrl { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let url_type_str = match self.url_type{ + let url_type_str = match self.url_type { UDP => "udp:/", PRUDP => "prudp:/", - PRUDPS => "prudps:/" + PRUDPS => "prudps:/", }; - write!(f, "{}",url_type_str)?; + write!(f, "{}", url_type_str)?; - for option in &self.options{ - match option{ + for option in &self.options { + match option { Address(v) => write!(f, "address={}", v)?, Port(v) => write!(f, "port={}", v)?, StreamType(v) => write!(f, "stream={}", v)?, @@ -168,15 +136,15 @@ impl Display for StationUrl{ } } -impl<'a> Into for &'a StationUrl{ +impl<'a> Into for &'a StationUrl { fn into(self) -> String { let url = self.to_string(); - - url[0..url.len()-1].into() + + url[0..url.len() - 1].into() } } -impl RmcSerialize for StationUrl{ +impl RmcSerialize for StationUrl { fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { let str = String::deserialize(reader)?; @@ -197,9 +165,9 @@ impl RmcSerialize for StationUrl{ } } -impl Debug for StationUrl{ +impl Debug for StationUrl { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let str: String = self.into(); f.write_str(&str) } -} \ No newline at end of file +} diff --git a/rnex-core/src/prudp/ticket.rs b/rnex-core/src/prudp/ticket.rs index 9fd094d..98e181f 100644 --- a/rnex-core/src/prudp/ticket.rs +++ b/rnex-core/src/prudp/ticket.rs @@ -2,7 +2,7 @@ use std::io::Cursor; use log::{error, info}; use rc4::{KeyInit, Rc4, Rc4Core, StreamCipher, cipher::StreamCipherCoreWrapper}; -use typenum::{U16, U32}; +use typenum::U16; use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; use crate::{ @@ -10,19 +10,17 @@ use crate::{ nex::account::Account, rmc::structures::RmcSerialize, }; +use rnex_core::PID; pub fn read_secure_connection_data( data: &[u8], act: &Account, -) -> Option<([u8; SESSION_KEY_LENGTH], u32, u32)> { +) -> Option<([u8; SESSION_KEY_LENGTH], PID, u32)> { let mut cursor = Cursor::new(data); let mut ticket_data: Vec = Vec::deserialize(&mut cursor).ok()?; let mut request_data: Vec = Vec::deserialize(&mut cursor).ok()?; - info!( - "done - request data" - ); + info!("done request data {}", SESSION_KEY_LENGTH); let ticket_data_size = ticket_data.len(); @@ -62,7 +60,7 @@ pub fn read_secure_connection_data( let mut reqest_data_cursor = Cursor::new(request_data); - let pid: u32 = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?; + let pid: PID = reqest_data_cursor.read_struct(IS_BIG_ENDIAN).ok()?; if pid != ticket_source_pid { let ticket_created_on = issued_time.to_regular_time(); diff --git a/rnex-core/src/rmc/protocols/account_management.rs b/rnex-core/src/rmc/protocols/account_management.rs new file mode 100644 index 0000000..541d1f6 --- /dev/null +++ b/rnex-core/src/rmc/protocols/account_management.rs @@ -0,0 +1,4 @@ +use macros::rmc_proto; + +#[rmc_proto(25)] +pub trait AccountManagement {} diff --git a/rnex-core/src/rmc/protocols/auth.rs b/rnex-core/src/rmc/protocols/auth.rs index 594d9dd..ca387f9 100644 --- a/rnex-core/src/rmc/protocols/auth.rs +++ b/rnex-core/src/rmc/protocols/auth.rs @@ -1,9 +1,21 @@ use crate::rmc::structures::connection_data::{ConnectionData, ConnectionDataOld}; +use cfg_if::cfg_if; use macros::{method_id, rmc_proto}; +use rnex_core::PID; use rnex_core::rmc::response::ErrorCode; use rnex_core::rmc::structures::any::Any; use rnex_core::rmc::structures::qresult::QResult; +cfg_if! { + if #[cfg(feature = "nx")]{ + type LOGIN_EX_RET = (QResult, PID, Vec, ConnectionData, String, String); + type REQUEST_TICKET_RET = (QResult, Vec, String); + } else { + type LOGIN_EX_RET = (QResult, PID, Vec, ConnectionData, String); + type REQUEST_TICKET_RET = (QResult, Vec); + } +} + /// This is the representation for `Ticket Granting`(for details see the /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) #[rmc_proto(10)] @@ -14,25 +26,22 @@ pub trait Auth { async fn login( &self, name: String, - ) -> Result<(QResult, u32, Vec, ConnectionDataOld, String), ErrorCode>; + ) -> Result<(QResult, PID, Vec, ConnectionDataOld, String), ErrorCode>; /// representation of the `LoginEx` method(for details see the /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) #[method_id(2)] - async fn login_ex( - &self, - name: String, - extra_data: Any, - ) -> Result<(QResult, u32, Vec, ConnectionData, String), ErrorCode>; + async fn login_ex(&self, name: String, extra_data: Any) -> Result; - /// representation of the `RequestTicket` method(for details see the - /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) #[method_id(3)] async fn request_ticket( &self, - source_pid: u32, - destination_pid: u32, - ) -> Result<(QResult, Vec), ErrorCode>; + source_pid: PID, + destination_pid: PID, + ) -> Result; + + /// representation of the `RequestTicket` method(for details see the + /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) /// representation of the `GetPID` method(for details see the /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) @@ -42,7 +51,7 @@ pub trait Auth { /// representation of the `LoginWithContext` method(for details see the /// [kinnay wiki entry](https://github.com/kinnay/NintendoClients/wiki/Authentication-Protocol)) #[method_id(5)] - async fn get_name(&self, pid: u32) -> Result; + async fn get_name(&self, pid: PID) -> Result; // `LoginWithContext` is left out here because we don't need it right now and versioning still // needs to be figured out diff --git a/rnex-core/src/rmc/protocols/matchmake.rs b/rnex-core/src/rmc/protocols/matchmake.rs index 595127a..94edce3 100644 --- a/rnex-core/src/rmc/protocols/matchmake.rs +++ b/rnex-core/src/rmc/protocols/matchmake.rs @@ -2,8 +2,10 @@ use macros::{method_id, rmc_proto}; use rnex_core::prudp::station_url::StationUrl; use rnex_core::rmc::response::ErrorCode; +use rnex_core::PID; + #[rmc_proto(21)] -pub trait Matchmake{ +pub trait Matchmake { #[method_id(2)] async fn unregister_gathering(&self, gid: u32) -> Result; #[method_id(41)] @@ -11,5 +13,10 @@ pub trait Matchmake{ #[method_id(42)] async fn update_session_host(&self, gid: u32, change_owner: bool) -> Result<(), ErrorCode>; #[method_id(44)] - async fn migrate_gathering_ownership(&self, gid: u32, candidates: Vec, participants_only: bool) -> Result<(), ErrorCode>; -} \ No newline at end of file + async fn migrate_gathering_ownership( + &self, + gid: u32, + candidates: Vec, + participants_only: bool, + ) -> Result<(), ErrorCode>; +} diff --git a/rnex-core/src/rmc/protocols/mod.rs b/rnex-core/src/rmc/protocols/mod.rs index 6896cf3..8feb2f3 100644 --- a/rnex-core/src/rmc/protocols/mod.rs +++ b/rnex-core/src/rmc/protocols/mod.rs @@ -1,5 +1,6 @@ #![allow(async_fn_in_trait)] +pub mod account_management; pub mod auth; pub mod friends; pub mod matchmake; diff --git a/rnex-core/src/rmc/protocols/notifications.rs b/rnex-core/src/rmc/protocols/notifications.rs index 164bf83..eef5c6c 100644 --- a/rnex-core/src/rmc/protocols/notifications.rs +++ b/rnex-core/src/rmc/protocols/notifications.rs @@ -1,19 +1,21 @@ -use macros::{method_id, rmc_proto, rmc_struct, RmcSerialize}; +use macros::{RmcSerialize, method_id, rmc_proto, rmc_struct}; -pub mod notification_types{ +use rnex_core::PID; + +pub mod notification_types { pub const OWNERSHIP_CHANGED: u32 = 4000; pub const HOST_CHANGED: u32 = 110000; } #[derive(RmcSerialize, Debug, Default, Clone)] #[rmc_struct(0)] -pub struct NotificationEvent{ - pub pid_source: u32, +pub struct NotificationEvent { + pub pid_source: PID, pub notif_type: u32, - pub param_1: u32, - pub param_2: u32, + pub param_1: PID, + pub param_2: PID, pub str_param: String, - pub param_3: u32, + pub param_3: PID, } #[rmc_proto(14, NoReturn)] @@ -21,4 +23,3 @@ pub trait Notification { #[method_id(1)] async fn process_notification_event(&self, event: NotificationEvent); } - diff --git a/rnex-core/src/rmc/protocols/secure.rs b/rnex-core/src/rmc/protocols/secure.rs index 3df23c6..503bf93 100644 --- a/rnex-core/src/rmc/protocols/secure.rs +++ b/rnex-core/src/rmc/protocols/secure.rs @@ -12,6 +12,7 @@ pub trait Secure { &self, station_urls: Vec, ) -> Result<(QResult, u32, StationUrl), ErrorCode>; + #[method_id(4)] async fn register_ex( &self, diff --git a/rnex-core/src/rmc/structures/matchmake.rs b/rnex-core/src/rmc/structures/matchmake.rs index 9194b97..a1518d1 100644 --- a/rnex-core/src/rmc/structures/matchmake.rs +++ b/rnex-core/src/rmc/structures/matchmake.rs @@ -1,14 +1,16 @@ +use macros::RmcSerialize; use rnex_core::kerberos::KerberosDateTime; use rnex_core::rmc::structures::variant::Variant; -use macros::RmcSerialize; + +use rnex_core::PID; // rmc structure #[derive(RmcSerialize, Debug, Clone, Default)] #[rmc_struct(0)] pub struct Gathering { pub self_gid: u32, - pub owner_pid: u32, - pub host_pid: u32, + pub owner_pid: PID, + pub host_pid: PID, pub minimum_participants: u16, pub maximum_participants: u16, pub participant_policy: u32, @@ -73,7 +75,7 @@ pub struct MatchmakeSessionSearchCriteria { #[rmc_struct(0)] pub struct AutoMatchmakeParam { pub matchmake_session: MatchmakeSession, - pub additional_participants: Vec, + pub additional_participants: Vec, pub gid_for_participation_check: u32, pub auto_matchmake_option: u32, pub join_message: String, @@ -86,7 +88,7 @@ pub struct AutoMatchmakeParam { #[rmc_struct(0)] pub struct CreateMatchmakeSessionParam { pub matchmake_session: MatchmakeSession, - pub additional_participants: Vec, + pub additional_participants: Vec, pub gid_for_participation_check: u32, pub create_matchmake_session_option: u32, pub join_message: String, @@ -103,7 +105,7 @@ pub struct MatchmakeBlockListParam { #[rmc_struct(0)] pub struct JoinMatchmakeSessionParam { pub gid: u32, - pub additional_participants: Vec, + pub additional_participants: Vec, pub gid_for_participation_check: u32, pub join_matchmake_session_open: u32, pub join_matchmake_session_behavior: u8, diff --git a/rnex-core/src/rmc/structures/networking.rs b/rnex-core/src/rmc/structures/networking.rs index 03627af..bc95b9e 100644 --- a/rnex-core/src/rmc/structures/networking.rs +++ b/rnex-core/src/rmc/structures/networking.rs @@ -1,9 +1,37 @@ -use std::io::{Read, Write}; -use std::net::{Ipv4Addr, SocketAddrV4}; +use crate::rmc::structures::{Error, Result, RmcSerialize}; use rnex_core::prudp::virtual_port::VirtualPort; -use crate::rmc::structures::RmcSerialize; +use std::io::{Read, Write}; +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; +use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; -impl RmcSerialize for SocketAddrV4{ +impl RmcSerialize for SocketAddr { + fn deserialize(reader: &mut impl std::io::Read) -> Result + where + Self: Sized, + { + let val: u8 = reader.read_struct(IS_BIG_ENDIAN)?; + match val { + 4 => Ok(SocketAddr::V4(SocketAddrV4::deserialize(reader)?)), + 6 => Ok(SocketAddr::V6(SocketAddrV6::deserialize(reader)?)), + v => Err(Error::UnexpectedValue(v as u64)), + } + } + fn serialize(&self, writer: &mut impl Write) -> Result<()> { + match self { + SocketAddr::V4(v) => { + writer.write_all(&[4])?; + v.serialize(writer)?; + } + SocketAddr::V6(v) => { + writer.write_all(&[6])?; + v.serialize(writer)?; + } + } + Ok(()) + } +} + +impl RmcSerialize for SocketAddrV4 { fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { self.ip().to_bits().serialize(writer)?; self.port().serialize(writer)?; @@ -21,9 +49,35 @@ impl RmcSerialize for SocketAddrV4{ Ok(6) } } +impl RmcSerialize for SocketAddrV6 { + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + self.ip().to_bits().serialize(writer)?; + self.port().serialize(writer)?; + self.flowinfo().serialize(writer)?; + self.scope_id().serialize(writer)?; + Ok(()) + } -impl RmcSerialize for VirtualPort{ + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + let ip = u128::deserialize(reader)?; + let port = u16::deserialize(reader)?; + let flowinfo = u32::deserialize(reader)?; + let scope_id = u32::deserialize(reader)?; + + Ok(SocketAddrV6::new( + Ipv6Addr::from_bits(ip), + port, + flowinfo, + scope_id, + )) + } + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(6) + } +} + +impl RmcSerialize for VirtualPort { fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { self.0.serialize(writer)?; @@ -36,4 +90,4 @@ impl RmcSerialize for VirtualPort{ fn serialize_write_size(&self) -> crate::rmc::structures::Result { Ok(1) } -} \ No newline at end of file +} diff --git a/rnex-core/src/rmc/structures/primitives.rs b/rnex-core/src/rmc/structures/primitives.rs index 3bdd835..e3888d4 100644 --- a/rnex-core/src/rmc/structures/primitives.rs +++ b/rnex-core/src/rmc/structures/primitives.rs @@ -1,5 +1,5 @@ use crate::rmc::structures::RmcSerialize; -use bytemuck::bytes_of; +use bytemuck::{bytes_of, bytes_of_mut}; use std::io::{Read, Write}; use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; @@ -110,6 +110,23 @@ impl RmcSerialize for u64 { } } +impl RmcSerialize for u128 { + #[inline(always)] + fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { + Ok(writer.write_all(bytes_of(self))?) + } + #[inline(always)] + fn deserialize(reader: &mut impl Read) -> crate::rmc::structures::Result { + let mut data = 0u128; + reader.read_exact(&mut bytes_of_mut(&mut data))?; + Ok(data) + } + #[inline(always)] + fn serialize_write_size(&self) -> crate::rmc::structures::Result { + Ok(8) + } +} + impl RmcSerialize for i64 { #[inline(always)] fn serialize(&self, writer: &mut impl Write) -> crate::rmc::structures::Result<()> { diff --git a/rnex-core/src/rmc/structures/ranking.rs b/rnex-core/src/rmc/structures/ranking.rs index 1782cba..778c5cf 100644 --- a/rnex-core/src/rmc/structures/ranking.rs +++ b/rnex-core/src/rmc/structures/ranking.rs @@ -20,55 +20,3 @@ struct UploadCompetitionData { struct UserData { name: [u16; 0x10], } - -#[cfg(test)] -mod test { - use crate::rmc::structures::ranking::{UploadCompetitionData, UserData}; - use bytemuck::from_bytes; - use rnex_core::rmc::structures::RmcSerialize; - use std::io::Cursor; - - #[test] - fn test() { - return; - let data: [u8; 0xBD] = [ - 0x00, 0xB8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0xA0, - 0x00, 0x00, 0x49, 0x00, 0x7A, 0x00, 0x7A, 0x00, 0x79, 0x00, 0x53, 0x00, 0x50, 0x00, - 0x46, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0xF2, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x1F, 0x5E, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x90, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x14, 0x87, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, - ]; - - let mut cursor = Cursor::new(data); - - let data = - UploadCompetitionData::deserialize(&mut cursor).expect("unable to deserialize data"); - - let user_data: &UserData = from_bytes(&data.player_data.0[..size_of::()]); - - let pos = user_data - .name - .iter() - .position(|v| *v == 0x0000) - .unwrap_or(0x10); - - let mut name = user_data.name[0..pos].to_vec(); - - name.iter_mut().for_each(|v| *v = v.swap_bytes()); - - let name = String::from_utf16(&name).expect("unable to get name"); - - println!("{:?}", name); - - assert!(u8::deserialize(&mut cursor).is_err()) - } -} diff --git a/rnex-core/src/rnex_proxy_common.rs b/rnex-core/src/rnex_proxy_common.rs index 29cb161..7f2c363 100644 --- a/rnex-core/src/rnex_proxy_common.rs +++ b/rnex-core/src/rnex_proxy_common.rs @@ -1,11 +1,9 @@ +use crate::{PID, prudp::socket_addr::PRUDPSockAddr}; use macros::RmcSerialize; -use crate::prudp::socket_addr::PRUDPSockAddr; #[derive(Debug, RmcSerialize)] #[rmc_struct(0)] -pub struct ConnectionInitData{ +pub struct ConnectionInitData { pub prudpsock_addr: PRUDPSockAddr, - pub pid: u32, - + pub pid: PID, } -