From 6127e13c836d06764d483b6b55188fd23a4314a2 Mon Sep 17 00:00:00 2001 From: Alex Yusiuk <55661041+RRRadicalEdward@users.noreply.github.com> Date: Wed, 24 Sep 2025 11:49:32 +0300 Subject: [PATCH 01/51] fix(web): fix this.lastSentClipboardData being nulled (#992) ```js await this.remoteDesktopService.onClipboardChanged(...) ``` Consumes the clipboard data we pass, so we need to clone the data to prevent `this.lastSentClipboardData` from being null. --- crates/iron-remote-desktop/src/lib.rs | 4 ++-- crates/iron-remote-desktop/src/session.rs | 2 +- crates/ironrdp-web/src/session.rs | 4 ++-- .../iron-remote-desktop/src/services/clipboard.service.ts | 4 ---- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/crates/iron-remote-desktop/src/lib.rs b/crates/iron-remote-desktop/src/lib.rs index 48367f94..5dd8dd60 100644 --- a/crates/iron-remote-desktop/src/lib.rs +++ b/crates/iron-remote-desktop/src/lib.rs @@ -159,8 +159,8 @@ macro_rules! make_bridge { } #[wasm_bindgen(js_name = onClipboardPaste)] - pub async fn on_clipboard_paste(&self, content: ClipboardData) -> Result<(), IronError> { - $crate::Session::on_clipboard_paste(&self.0, content.0) + pub async fn on_clipboard_paste(&self, content: &ClipboardData) -> Result<(), IronError> { + $crate::Session::on_clipboard_paste(&self.0, &content.0) .await .map_err(IronError) } diff --git a/crates/iron-remote-desktop/src/session.rs b/crates/iron-remote-desktop/src/session.rs index afe9f2d5..d9f973d8 100644 --- a/crates/iron-remote-desktop/src/session.rs +++ b/crates/iron-remote-desktop/src/session.rs @@ -84,7 +84,7 @@ pub trait Session { fn on_clipboard_paste( &self, - content: Self::ClipboardData, + content: &Self::ClipboardData, ) -> impl core::future::Future>; fn resize( diff --git a/crates/ironrdp-web/src/session.rs b/crates/ironrdp-web/src/session.rs index e5933bad..57338973 100644 --- a/crates/ironrdp-web/src/session.rs +++ b/crates/ironrdp-web/src/session.rs @@ -800,10 +800,10 @@ impl iron_remote_desktop::Session for Session { Ok(()) } - async fn on_clipboard_paste(&self, content: Self::ClipboardData) -> Result<(), Self::Error> { + async fn on_clipboard_paste(&self, content: &Self::ClipboardData) -> Result<(), Self::Error> { self.input_events_tx .unbounded_send(RdpInputEvent::ClipboardBackend( - WasmClipboardBackendMessage::LocalClipboardChanged(content), + WasmClipboardBackendMessage::LocalClipboardChanged(content.clone()), )) .context("Send clipboard backend event")?; diff --git a/web-client/iron-remote-desktop/src/services/clipboard.service.ts b/web-client/iron-remote-desktop/src/services/clipboard.service.ts index 98c8e7cc..5bae0927 100644 --- a/web-client/iron-remote-desktop/src/services/clipboard.service.ts +++ b/web-client/iron-remote-desktop/src/services/clipboard.service.ts @@ -149,7 +149,6 @@ export class ClipboardService { if (!clipboardData.isEmpty()) { this.lastSentClipboardData = clipboardData; - // TODO(Fix): onClipboardChanged takes an ownership over clipboardData, so lastSentClipboardData will be nullptr. await this.remoteDesktopService.onClipboardChanged(clipboardData); } } @@ -192,7 +191,6 @@ export class ClipboardService { // This callback is required to send initial clipboard state if available. private onForceClipboardUpdate() { - // TODO(Fix): lastSentClipboardData is nullptr. try { if (this.lastSentClipboardData) { this.remoteDesktopService.onClipboardChanged(this.lastSentClipboardData); @@ -313,7 +311,6 @@ export class ClipboardService { if (!clipboardData.isEmpty()) { this.lastSentClipboardData = clipboardData; - // TODO(Fix): onClipboardChanged takes an ownership over clipboardData, so lastSentClipboardData will be nullptr. await this.remoteDesktopService.onClipboardChanged(clipboardData); } } @@ -406,7 +403,6 @@ export class ClipboardService { if (!clipboardData.isEmpty()) { this.lastSentClipboardData = clipboardData; - // TODO(Fix): onClipboardChanged takes an ownership over clipboardData, so lastSentClipboardData will be nullptr. await this.remoteDesktopService.onClipboardChanged(clipboardData); } } From 3198abae2f6f35e018722140dd6b27d63a2eaa1f Mon Sep 17 00:00:00 2001 From: Vladyslav Nikonov Date: Thu, 25 Sep 2025 12:52:17 +0000 Subject: [PATCH 02/51] chore(release): prepare for Devolutions.IronRdp v2025.09.24.0 (#998) --- ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.csproj b/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.csproj index 00ba0ce5..851cddb6 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.csproj +++ b/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.csproj @@ -4,7 +4,7 @@ Devolutions Bindings to Rust IronRDP native library latest - 2024.10.9.0 + 2025.09.24.0 enable enable true From a660d7f96098e23e0d315f17c8194208c9c11632 Mon Sep 17 00:00:00 2001 From: Vladyslav Nikonov Date: Mon, 29 Sep 2025 10:39:28 +0000 Subject: [PATCH 03/51] chore(release): remove leading zero from nuget version (#1000) leading zero in Nuget version caused CI to fail on ffi crate build: ``` error: invalid leading zero in minor version number --> ffi/Cargo.toml:3:11 | 3 | version = "2025.09.24" | ^^^^^^^^^^^^ | error: failed to load manifest for workspace member `/Users/runner/work/IronRDP/IronRDP/ffi` referenced by workspace at `/Users/runner/work/IronRDP/IronRDP/Cargo.toml` ``` --- ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.csproj b/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.csproj index 851cddb6..5ae81afe 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.csproj +++ b/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.csproj @@ -4,7 +4,7 @@ Devolutions Bindings to Rust IronRDP native library latest - 2025.09.24.0 + 2025.9.24.0 enable enable true From 209108dc2c9d8d24535597fe6e4d6a3228c85444 Mon Sep 17 00:00:00 2001 From: devolutionsbot <31221910+devolutionsbot@users.noreply.github.com> Date: Mon, 29 Sep 2025 07:07:48 -0400 Subject: [PATCH 04/51] chore(release): prepare for publishing (#997) --- Cargo.lock | 181 ++++++++++++------------ crates/iron-remote-desktop/CHANGELOG.md | 6 + crates/iron-remote-desktop/Cargo.toml | 2 +- fuzz/Cargo.lock | 20 +-- 4 files changed, 108 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9d284edf..e29c68ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "ab_glyph" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e074464580a518d16a7126262fffaaa47af89d4099d4cb403f8ed938ba12ee7d" +checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -20,9 +20,9 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "addr2line" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ "gimli", ] @@ -264,7 +264,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -345,9 +345,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.14.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b8ff6c09cd57b16da53641caa860168b88c172a5ee163b0288d3d6eea12786" +checksum = "879b6c89592deb404ba4dc0ae6b58ffd1795c78991cbb5b8bc441c48a070440d" dependencies = [ "aws-lc-sys", "zeroize", @@ -355,22 +355,23 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.31.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e44d16778acaf6a9ec9899b92cebd65580b83f685446bf2e1f5d3d732f99dcd" +checksum = "1ba2e2516bdf37af57fc6ff047855f54abad0066e5c4fdaaeb76dabb2e05bcf5" dependencies = [ "bindgen", "cc", "cmake", "dunce", "fs_extra", + "libloading", ] [[package]] name = "backtrace" -version = "0.3.75" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ "addr2line", "cfg-if", @@ -378,7 +379,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-link 0.2.0", ] [[package]] @@ -607,9 +608,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.38" +version = "1.2.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" +checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f" dependencies = [ "find-msvc-tools", "jobserver", @@ -1168,9 +1169,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" +checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" dependencies = [ "powerfmt", ] @@ -1484,7 +1485,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.0", + "windows-sys 0.61.1", ] [[package]] @@ -1534,7 +1535,7 @@ dependencies = [ "ironrdp-core", "ironrdp-dvc-pipe-proxy", "sspi", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", "tracing-subscriber", ] @@ -1800,9 +1801,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.1" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" [[package]] name = "glob" @@ -2113,7 +2114,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.0", + "windows-core 0.62.1", ] [[package]] @@ -2320,7 +2321,7 @@ dependencies = [ [[package]] name = "iron-remote-desktop" -version = "0.6.0" +version = "0.7.0" dependencies = [ "console_error_panic_hook", "tracing", @@ -2651,7 +2652,7 @@ dependencies = [ "pkcs1", "sha1", "tap", - "thiserror 2.0.16", + "thiserror 2.0.17", "x509-cert", ] @@ -2952,9 +2953,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.80" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" dependencies = [ "once_cell", "wasm-bindgen", @@ -2986,12 +2987,12 @@ checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" [[package]] name = "libloading" -version = "0.8.9" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-link 0.2.0", + "windows-targets 0.53.4", ] [[package]] @@ -3128,9 +3129,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memmap2" @@ -3625,9 +3626,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.7" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "memchr", ] @@ -3884,9 +3885,9 @@ dependencies = [ [[package]] name = "picky-asn1-der" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dccb53c26f70c082e008818f524bd45d057069517b047bd0c0ee062d6d7d7f2" +checksum = "f1c9c33310628f2e1758bbde11b16713505e188a859ab7990f98eb015b943bf5" dependencies = [ "picky-asn1", "serde", @@ -4051,7 +4052,7 @@ dependencies = [ "hermit-abi", "pin-project-lite", "rustix 1.1.2", - "windows-sys 0.61.0", + "windows-sys 0.61.1", ] [[package]] @@ -4219,7 +4220,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2 0.6.0", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", "web-time", @@ -4240,7 +4241,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.16", + "thiserror 2.0.17", "tinyvec", "tracing", "web-time", @@ -4262,9 +4263,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] @@ -4404,9 +4405,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.2" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" dependencies = [ "aho-corasick", "memchr", @@ -4416,9 +4417,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" dependencies = [ "aho-corasick", "memchr", @@ -4633,7 +4634,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.0", + "windows-sys 0.61.1", ] [[package]] @@ -4661,7 +4662,7 @@ dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.5.0", + "security-framework 3.5.1", ] [[package]] @@ -4734,7 +4735,7 @@ version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.61.0", + "windows-sys 0.61.1", ] [[package]] @@ -4791,9 +4792,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.5.0" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc198e42d9b7510827939c9a15f5062a0c913f3371d765977e586d2fe6c16f4a" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ "bitflags 2.9.4", "core-foundation 0.10.1", @@ -4820,9 +4821,9 @@ checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "serde" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", @@ -4840,18 +4841,18 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -5255,7 +5256,7 @@ dependencies = [ "getrandom 0.3.3", "once_cell", "rustix 1.1.2", - "windows-sys 0.61.0", + "windows-sys 0.61.1", ] [[package]] @@ -5269,11 +5270,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.16", + "thiserror-impl 2.0.17", ] [[package]] @@ -5289,9 +5290,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", @@ -5482,9 +5483,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f63835928ca123f1bef57abbcd23bb2ba0ac9ae1235f1e65bda0d06e7786bd" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", @@ -5736,7 +5737,7 @@ dependencies = [ "rustls", "rustls-pki-types", "sha1", - "thiserror 2.0.16", + "thiserror 2.0.17", "utf-8", ] @@ -5937,9 +5938,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" +checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" dependencies = [ "cfg-if", "once_cell", @@ -5950,9 +5951,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" +checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" dependencies = [ "bumpalo", "log", @@ -5964,9 +5965,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.53" +version = "0.4.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0b221ff421256839509adbb55998214a70d829d3a28c69b4a6672e9d2a42f67" +checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" dependencies = [ "cfg-if", "js-sys", @@ -5977,9 +5978,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" +checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5987,9 +5988,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" +checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ "proc-macro2", "quote", @@ -6000,9 +6001,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.103" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" +checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" dependencies = [ "unicode-ident", ] @@ -6118,9 +6119,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.80" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbe734895e869dc429d78c4b433f8d17d95f8d05317440b4fad5ab2d33e596dc" +checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" dependencies = [ "js-sys", "wasm-bindgen", @@ -6184,7 +6185,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.0", + "windows-sys 0.61.1", ] [[package]] @@ -6250,9 +6251,9 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.62.0" +version = "0.62.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" +checksum = "6844ee5416b285084d3d3fffd743b925a6c9385455f64f6d4fa3031c4c2749a9" dependencies = [ "windows-implement", "windows-interface", @@ -6274,9 +6275,9 @@ dependencies = [ [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.60.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "edb307e42a74fb6de9bf3a02d9712678b22399c87e6fa869d6dfcd8c1b7754e0" dependencies = [ "proc-macro2", "quote", @@ -6285,9 +6286,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "c0abd1ddbc6964ac14db11c7213d6532ef34bd9aa042c2e5935f59d7908b46a5" dependencies = [ "proc-macro2", "quote", @@ -6414,14 +6415,14 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.3", + "windows-targets 0.53.4", ] [[package]] name = "windows-sys" -version = "0.61.0" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" dependencies = [ "windows-link 0.2.0", ] @@ -6474,11 +6475,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.3" +version = "0.53.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +checksum = "2d42b7b7f66d2a06854650af09cfdf8713e427a439c97ad65a6375318033ac4b" dependencies = [ - "windows-link 0.1.3", + "windows-link 0.2.0", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", diff --git a/crates/iron-remote-desktop/CHANGELOG.md b/crates/iron-remote-desktop/CHANGELOG.md index fa04f3b3..2f076f11 100644 --- a/crates/iron-remote-desktop/CHANGELOG.md +++ b/crates/iron-remote-desktop/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.7.0](https://github.com/Devolutions/IronRDP/compare/iron-remote-desktop-v0.6.0...iron-remote-desktop-v0.7.0)] - 2025-09-29 + +### Bug Fixes + +- [**breaking**] Changed onClipboardChanged to not consume the input (#992) ([6127e13c83](https://github.com/Devolutions/IronRDP/commit/6127e13c836d06764d483b6b55188fd23a4314a2)) + ## [[0.6.0](https://github.com/Devolutions/IronRDP/compare/iron-remote-desktop-v0.5.0...iron-remote-desktop-v0.6.0)] - 2025-08-29 ### Features diff --git a/crates/iron-remote-desktop/Cargo.toml b/crates/iron-remote-desktop/Cargo.toml index 7c082782..ba5d8a90 100644 --- a/crates/iron-remote-desktop/Cargo.toml +++ b/crates/iron-remote-desktop/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iron-remote-desktop" -version = "0.6.0" +version = "0.7.0" readme = "README.md" description = "Helper crate for building WASM modules compatible with iron-remote-desktop WebComponent" edition.workspace = true diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index f1fe432d..757a62b9 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -108,9 +108,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.2.38" +version = "1.2.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80f41ae168f955c12fb8960b057d70d0ca153fb83182b57d86380443527be7e9" +checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f" dependencies = [ "find-msvc-tools", "jobserver", @@ -481,9 +481,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "minimal-lexical" @@ -596,9 +596,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] @@ -693,18 +693,18 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "thiserror" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", From b91a4eeb010cee395ed34e5a28423842244e6bdc Mon Sep 17 00:00:00 2001 From: Alex Yusiuk <55661041+RRRadicalEdward@users.noreply.github.com> Date: Tue, 30 Sep 2025 16:45:22 +0300 Subject: [PATCH 05/51] refactor: enable `redundant_type_annotations` clippy lint (#1001) --- Cargo.toml | 1 + crates/ironrdp-mstsgu/src/lib.rs | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ba8229bd..bee48cd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -141,6 +141,7 @@ string_add = "warn" range_plus_one = "warn" # TODO: self_named_module_files = "warn" # TODO: partial_pub_fields = "warn" (should we enable only in pdu crates?) +redundant_type_annotations = "warn" # == Compile-time / optimization == # doc_include_without_cfg = "warn" diff --git a/crates/ironrdp-mstsgu/src/lib.rs b/crates/ironrdp-mstsgu/src/lib.rs index 0eb078e5..92430080 100644 --- a/crates/ironrdp-mstsgu/src/lib.rs +++ b/crates/ironrdp-mstsgu/src/lib.rs @@ -200,8 +200,7 @@ impl GwClient { let work = tokio::spawn(async move { let iv = Duration::from_secs(15 * 60); - let mut keepalive_interval: tokio::time::Interval = - tokio::time::interval_at(tokio::time::Instant::now() + iv, iv); + let mut keepalive_interval = tokio::time::interval_at(tokio::time::Instant::now() + iv, iv); loop { let mut wsbuf = [0u8; 8192]; From 18c81ed5d8d3bf13b3d10fe15209233c0c10bb62 Mon Sep 17 00:00:00 2001 From: Gabriel Bauman <967743+gabrielbauman@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:54:37 -0700 Subject: [PATCH 06/51] feat(web): human-readable descriptions for RDCleanPath errors (#999) More munging to give human-readable webclient-side errors for RDCleanPath general/negotiation errors, including strings for WSA and TLS and HTTP error conditions. --- crates/ironrdp-rdcleanpath/src/lib.rs | 118 +++++++++++++++++- .../tests/rdcleanpath.rs | 64 +++++++++- crates/ironrdp-web/src/error.rs | 2 +- 3 files changed, 178 insertions(+), 6 deletions(-) diff --git a/crates/ironrdp-rdcleanpath/src/lib.rs b/crates/ironrdp-rdcleanpath/src/lib.rs index 7deb4c67..b884ba85 100644 --- a/crates/ironrdp-rdcleanpath/src/lib.rs +++ b/crates/ironrdp-rdcleanpath/src/lib.rs @@ -30,18 +30,128 @@ pub struct RDCleanPathErr { impl fmt::Display for RDCleanPathErr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "RDCleanPath error (code {})", self.error_code)?; + let error_description = match self.error_code { + GENERAL_ERROR_CODE => "general error", + NEGOTIATION_ERROR_CODE => "negotiation error", + _ => "unknown error", + }; + write!(f, "{error_description} (code {})", self.error_code)?; if let Some(http_status_code) = self.http_status_code { - write!(f, " [HTTP status = {http_status_code}]")?; + let description = match http_status_code { + 200 => "OK", + 400 => "bad request", + 401 => "unauthorized", + 403 => "forbidden", + 404 => "not found", + 405 => "method not allowed", + 408 => "request timeout", + 409 => "conflict", + 410 => "gone", + 413 => "payload too large", + 414 => "URI too long", + 422 => "unprocessable entity", + 429 => "too many requests", + 500 => "internal server error", + 501 => "not implemented", + 502 => "bad gateway", + 503 => "service unavailable", + 504 => "gateway timeout", + 505 => "HTTP version not supported", + _ => "unknown HTTP status", + }; + write!(f, "; HTTP {http_status_code} {description}")?; } if let Some(wsa_last_error) = self.wsa_last_error { - write!(f, " [WSA last error = {wsa_last_error}]")?; + let description = match wsa_last_error { + 10004 => "interrupted system call", + 10009 => "bad file descriptor", + 10013 => "permission denied", + 10014 => "bad address", + 10022 => "invalid argument", + 10024 => "too many open files", + 10035 => "resource temporarily unavailable", + 10036 => "operation now in progress", + 10037 => "operation already in progress", + 10038 => "socket operation on nonsocket", + 10039 => "destination address required", + 10040 => "message too long", + 10041 => "protocol wrong type for socket", + 10042 => "bad protocol option", + 10043 => "protocol not supported", + 10044 => "socket type not supported", + 10045 => "operation not supported", + 10046 => "protocol family not supported", + 10047 => "address family not supported by protocol family", + 10048 => "address already in use", + 10049 => "cannot assign requested address", + 10050 => "network is down", + 10051 => "network is unreachable", + 10052 => "network dropped connection on reset", + 10053 => "software caused connection abort", + 10054 => "connection reset by peer", + 10055 => "no buffer space available", + 10056 => "socket is already connected", + 10057 => "socket is not connected", + 10058 => "cannot send after socket shutdown", + 10060 => "connection timed out", + 10061 => "connection refused", + 10064 => "host is down", + 10065 => "no route to host", + 10067 => "too many processes", + 10091 => "network subsystem is unavailable", + 10092 => "Winsock version not supported", + 10093 => "successful WSAStartup not yet performed", + 10101 => "graceful shutdown in progress", + 10109 => "class type not found", + 11001 => "host not found", + 11002 => "nonauthoritative host not found", + 11003 => "this is a nonrecoverable error", + 11004 => "valid name, no data record of requested type", + _ => "unknown WSA error", + }; + write!(f, "; WSA {wsa_last_error} {description}")?; } if let Some(tls_alert_code) = self.tls_alert_code { - write!(f, " [TLS alert = {tls_alert_code}]")?; + let description = match tls_alert_code { + 0 => "close notify", + 10 => "unexpected message", + 20 => "bad record MAC", + 21 => "decryption failed", + 22 => "record overflow", + 30 => "decompression failure", + 40 => "handshake failure", + 41 => "no certificate", + 42 => "bad certificate", + 43 => "unsupported certificate", + 44 => "certificate revoked", + 45 => "certificate expired", + 46 => "certificate unknown", + 47 => "illegal parameter", + 48 => "unknown CA", + 49 => "access denied", + 50 => "decode error", + 51 => "decrypt error", + 60 => "export restriction", + 70 => "protocol version", + 71 => "insufficient security", + 80 => "internal error", + 90 => "user canceled", + 100 => "no renegotiation", + 109 => "missing extension", + 110 => "unsupported extension", + 111 => "certificate unobtainable", + 112 => "unrecognized name", + 113 => "bad certificate status response", + 114 => "bad certificate hash value", + 115 => "unknown PSK identity", + 116 => "certificate required", + 120 => "no application protocol", + _ => "unknown TLS alert", + }; + write!(f, "; TLS alert {tls_alert_code} {description}")?; } Ok(()) diff --git a/crates/ironrdp-testsuite-core/tests/rdcleanpath.rs b/crates/ironrdp-testsuite-core/tests/rdcleanpath.rs index 4c91f3b3..9c4cf28d 100644 --- a/crates/ironrdp-testsuite-core/tests/rdcleanpath.rs +++ b/crates/ironrdp-testsuite-core/tests/rdcleanpath.rs @@ -1,4 +1,7 @@ -use ironrdp_rdcleanpath::{DetectionResult, RDCleanPathPdu, VERSION_1}; +use expect_test::{expect, Expect}; +use ironrdp_rdcleanpath::{ + DetectionResult, RDCleanPathErr, RDCleanPathPdu, GENERAL_ERROR_CODE, NEGOTIATION_ERROR_CODE, VERSION_1, +}; use rstest::rstest; fn request() -> RDCleanPathPdu { @@ -123,3 +126,62 @@ fn detect_not_enough(#[case] payload: &[u8]) { let result = RDCleanPathPdu::detect(payload); assert_eq!(result, DetectionResult::NotEnoughBytes); } + +#[rstest] +#[case::http( + RDCleanPathErr { + error_code: GENERAL_ERROR_CODE, + http_status_code: Some(404), + wsa_last_error: None, + tls_alert_code: None, + }, + expect!["general error (code 1); HTTP 404 not found"], +)] +#[case::wsa( + RDCleanPathErr { + error_code: GENERAL_ERROR_CODE, + http_status_code: None, + wsa_last_error: Some(10061), + tls_alert_code: None, + }, + expect!["general error (code 1); WSA 10061 connection refused"], +)] +#[case::tls( + RDCleanPathErr { + error_code: GENERAL_ERROR_CODE, + http_status_code: None, + wsa_last_error: None, + tls_alert_code: Some(40), + }, + expect!["general error (code 1); TLS alert 40 handshake failure"], +)] +#[case::nego( + RDCleanPathErr { + error_code: NEGOTIATION_ERROR_CODE, + http_status_code: None, + wsa_last_error: None, + tls_alert_code: None, + }, + expect!["negotiation error (code 2)"], +)] +#[case::combined( + RDCleanPathErr { + error_code: GENERAL_ERROR_CODE, + http_status_code: Some(502), + wsa_last_error: Some(10060), + tls_alert_code: Some(45), + }, + expect!["general error (code 1); HTTP 502 bad gateway; WSA 10060 connection timed out; TLS alert 45 certificate expired"], +)] +#[case::unknown_codes( + RDCleanPathErr { + error_code: 99, + http_status_code: Some(999), + wsa_last_error: Some(65000), + tls_alert_code: Some(255), + }, + expect!["unknown error (code 99); HTTP 999 unknown HTTP status; WSA 65000 unknown WSA error; TLS alert 255 unknown TLS alert"], +)] +fn error_display(#[case] error: RDCleanPathErr, #[case] expected: Expect) { + expected.assert_eq(&error.to_string()); +} diff --git a/crates/ironrdp-web/src/error.rs b/crates/ironrdp-web/src/error.rs index 4e159ef9..3264ae48 100644 --- a/crates/ironrdp-web/src/error.rs +++ b/crates/ironrdp-web/src/error.rs @@ -15,7 +15,7 @@ impl IronError { impl iron_remote_desktop::IronError for IronError { fn backtrace(&self) -> String { - format!("{:?}", self.source) + format!("{:#}", self.source) } fn kind(&self) -> IronErrorKind { From c6b5487559ea54b2e6f2421efbd986758df63227 Mon Sep 17 00:00:00 2001 From: devolutionsbot <31221910+devolutionsbot@users.noreply.github.com> Date: Wed, 1 Oct 2025 23:34:02 -0400 Subject: [PATCH 07/51] chore(release): prepare for publishing (#1002) --- Cargo.lock | 22 +++++++++++----------- crates/ironrdp-rdcleanpath/CHANGELOG.md | 10 ++++++++++ crates/ironrdp-rdcleanpath/Cargo.toml | 2 +- fuzz/Cargo.lock | 8 ++++---- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e29c68ca..78d07bb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -180,9 +180,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" @@ -355,9 +355,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba2e2516bdf37af57fc6ff047855f54abad0066e5c4fdaaeb76dabb2e05bcf5" +checksum = "a2b715a6010afb9e457ca2b7c9d2b9c344baa8baed7b38dc476034c171b32575" dependencies = [ "bindgen", "cc", @@ -2669,7 +2669,7 @@ dependencies = [ [[package]] name = "ironrdp-rdcleanpath" -version = "0.2.0" +version = "0.2.1" dependencies = [ "der", ] @@ -4686,9 +4686,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.6" +version = "0.103.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" +checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" dependencies = [ "aws-lc-rs", "ring", @@ -6919,9 +6919,9 @@ dependencies = [ [[package]] name = "yuv" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3bb136c6b36d2856e62f3121892ae59f32caf5b0a9ff184600f0f5f4d5ff075" +checksum = "30078f3e5790a2127f89c57c4ccb46d060205bf2c4c267f77cd08fb5c02c6d79" dependencies = [ "num-traits", ] @@ -6969,9 +6969,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" dependencies = [ "zeroize_derive", ] diff --git a/crates/ironrdp-rdcleanpath/CHANGELOG.md b/crates/ironrdp-rdcleanpath/CHANGELOG.md index fa6410ca..071e3893 100644 --- a/crates/ironrdp-rdcleanpath/CHANGELOG.md +++ b/crates/ironrdp-rdcleanpath/CHANGELOG.md @@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.2.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdcleanpath-v0.2.0...ironrdp-rdcleanpath-v0.2.1)] - 2025-10-02 + +### Features + +- Human-readable descriptions for RDCleanPath errors (#999) ([18c81ed5d8](https://github.com/Devolutions/IronRDP/commit/18c81ed5d8d3bf13b3d10fe15209233c0c10bb62)) + + More munging to give human-readable webclient-side errors for + RDCleanPath general/negotiation errors, including strings for WSA and + TLS and HTTP error conditions. + ## [[0.2.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdcleanpath-v0.1.3...ironrdp-rdcleanpath-v0.2.0)] - 2025-08-29 ### Features diff --git a/crates/ironrdp-rdcleanpath/Cargo.toml b/crates/ironrdp-rdcleanpath/Cargo.toml index 9e47b1fa..3c3c73dd 100644 --- a/crates/ironrdp-rdcleanpath/Cargo.toml +++ b/crates/ironrdp-rdcleanpath/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-rdcleanpath" -version = "0.2.0" +version = "0.2.1" readme = "README.md" description = "RDCleanPath PDU structure used by IronRDP web client and Devolutions Gateway" edition.workspace = true diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 757a62b9..a6f3ad32 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -829,18 +829,18 @@ dependencies = [ [[package]] name = "yuv" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3bb136c6b36d2856e62f3121892ae59f32caf5b0a9ff184600f0f5f4d5ff075" +checksum = "30078f3e5790a2127f89c57c4ccb46d060205bf2c4c267f77cd08fb5c02c6d79" dependencies = [ "num-traits", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" dependencies = [ "zeroize_derive", ] From 49a0a9e6d20b741efb5acf5a694d9213a534b84b Mon Sep 17 00:00:00 2001 From: Alex Yusiuk <55661041+RRRadicalEdward@users.noreply.github.com> Date: Thu, 2 Oct 2025 11:05:34 +0300 Subject: [PATCH 08/51] chore: enable `rest_pat_in_fully_bound_structs` clippy lint (#1003) --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index bee48cd3..e6c94d86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -142,6 +142,7 @@ range_plus_one = "warn" # TODO: self_named_module_files = "warn" # TODO: partial_pub_fields = "warn" (should we enable only in pdu crates?) redundant_type_annotations = "warn" +rest_pat_in_fully_bound_structs = "warn" # == Compile-time / optimization == # doc_include_without_cfg = "warn" From bbc38db75011e48edf436e759bf9b3aa50e72817 Mon Sep 17 00:00:00 2001 From: Alex Yusiuk <55661041+RRRadicalEdward@users.noreply.github.com> Date: Thu, 2 Oct 2025 11:34:02 +0300 Subject: [PATCH 09/51] chore: enable `try_err` clippy lint (#1004) --- Cargo.toml | 1 + crates/ironrdp-cliprdr-native/src/windows.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e6c94d86..1d894d3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -142,6 +142,7 @@ range_plus_one = "warn" # TODO: self_named_module_files = "warn" # TODO: partial_pub_fields = "warn" (should we enable only in pdu crates?) redundant_type_annotations = "warn" +try_err = "warn" rest_pat_in_fully_bound_structs = "warn" # == Compile-time / optimization == # diff --git a/crates/ironrdp-cliprdr-native/src/windows.rs b/crates/ironrdp-cliprdr-native/src/windows.rs index b91241c5..b488cfb5 100644 --- a/crates/ironrdp-cliprdr-native/src/windows.rs +++ b/crates/ironrdp-cliprdr-native/src/windows.rs @@ -162,7 +162,7 @@ impl WinClipboard { // SAFETY: low-level WinAPI call let atom = unsafe { RegisterClassA(&wc) }; if atom == 0 { - return Err(Error::from_win32())?; + return Err(WinCliprdrError::from(Error::from_win32())); } // SAFETY: low-level WinAPI call @@ -184,7 +184,7 @@ impl WinClipboard { }; if window.is_invalid() { - return Err(Error::from_win32())?; + return Err(WinCliprdrError::from(Error::from_win32())); } // Init clipboard processing for WinAPI event loop // From a8289bf63f96590beaa4bd44ad75960945fb181d Mon Sep 17 00:00:00 2001 From: Alex Yusiuk <55661041+RRRadicalEdward@users.noreply.github.com> Date: Thu, 2 Oct 2025 12:10:55 +0300 Subject: [PATCH 10/51] refactor: enable `unnecessary_self_imports` clippy lint (#1005) --- Cargo.toml | 1 + .../ironrdp-acceptor/src/channel_connection.rs | 11 +++++------ crates/ironrdp-acceptor/src/finalization.rs | 17 ++++++++--------- .../src/connection_activation.rs | 2 +- crates/ironrdp-server/src/helper.rs | 3 +-- crates/ironrdp-tls/src/rustls.rs | 2 +- 6 files changed, 17 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1d894d3f..d6271f8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -142,6 +142,7 @@ range_plus_one = "warn" # TODO: self_named_module_files = "warn" # TODO: partial_pub_fields = "warn" (should we enable only in pdu crates?) redundant_type_annotations = "warn" +unnecessary_self_imports = "warn" try_err = "warn" rest_pat_in_fully_bound_structs = "warn" diff --git a/crates/ironrdp-acceptor/src/channel_connection.rs b/crates/ironrdp-acceptor/src/channel_connection.rs index 2c6aa62e..62bd6cd3 100644 --- a/crates/ironrdp-acceptor/src/channel_connection.rs +++ b/crates/ironrdp-acceptor/src/channel_connection.rs @@ -4,9 +4,8 @@ use ironrdp_connector::{ reason_err, ConnectorError, ConnectorErrorExt as _, ConnectorResult, Sequence, State, Written, }; use ironrdp_core::WriteBuf; +use ironrdp_pdu::mcs; use ironrdp_pdu::x224::X224; -use ironrdp_pdu::{self as pdu}; -use pdu::mcs; use tracing::debug; #[derive(Debug)] @@ -57,13 +56,13 @@ impl State for ChannelConnectionState { } impl Sequence for ChannelConnectionSequence { - fn next_pdu_hint(&self) -> Option<&dyn pdu::PduHint> { + fn next_pdu_hint(&self) -> Option<&dyn ironrdp_pdu::PduHint> { match &self.state { ChannelConnectionState::Consumed => None, - ChannelConnectionState::WaitErectDomainRequest => Some(&pdu::X224_HINT), - ChannelConnectionState::WaitAttachUserRequest => Some(&pdu::X224_HINT), + ChannelConnectionState::WaitErectDomainRequest => Some(&ironrdp_pdu::X224_HINT), + ChannelConnectionState::WaitAttachUserRequest => Some(&ironrdp_pdu::X224_HINT), ChannelConnectionState::SendAttachUserConfirm => None, - ChannelConnectionState::WaitChannelJoinRequest { .. } => Some(&pdu::X224_HINT), + ChannelConnectionState::WaitChannelJoinRequest { .. } => Some(&ironrdp_pdu::X224_HINT), ChannelConnectionState::SendChannelJoinConfirm { .. } => None, ChannelConnectionState::AllJoined => None, } diff --git a/crates/ironrdp-acceptor/src/finalization.rs b/crates/ironrdp-acceptor/src/finalization.rs index 6192ebbf..2560e530 100644 --- a/crates/ironrdp-acceptor/src/finalization.rs +++ b/crates/ironrdp-acceptor/src/finalization.rs @@ -1,8 +1,7 @@ use ironrdp_connector::{ConnectorError, ConnectorErrorExt as _, ConnectorResult, Sequence, State, Written}; use ironrdp_core::WriteBuf; +use ironrdp_pdu::rdp; use ironrdp_pdu::x224::X224; -use ironrdp_pdu::{self as pdu}; -use pdu::rdp; use tracing::debug; use crate::util::{self, wrap_share_data}; @@ -60,13 +59,13 @@ impl State for FinalizationState { } impl Sequence for FinalizationSequence { - fn next_pdu_hint(&self) -> Option<&dyn pdu::PduHint> { + fn next_pdu_hint(&self) -> Option<&dyn ironrdp_pdu::PduHint> { match &self.state { FinalizationState::Consumed => None, - FinalizationState::WaitSynchronize => Some(&pdu::X224Hint), - FinalizationState::WaitControlCooperate => Some(&pdu::X224Hint), - FinalizationState::WaitRequestControl => Some(&pdu::X224Hint), - FinalizationState::WaitFontList => Some(&pdu::RdpHint), + FinalizationState::WaitSynchronize => Some(&ironrdp_pdu::X224Hint), + FinalizationState::WaitControlCooperate => Some(&ironrdp_pdu::X224Hint), + FinalizationState::WaitRequestControl => Some(&ironrdp_pdu::X224Hint), + FinalizationState::WaitFontList => Some(&ironrdp_pdu::RdpHint), FinalizationState::SendSynchronizeConfirm => None, FinalizationState::SendControlCooperateConfirm => None, FinalizationState::SendGrantedControlConfirm => None, @@ -221,7 +220,7 @@ fn create_font_map() -> rdp::headers::ShareDataPdu { } fn decode_share_control(input: &[u8]) -> ConnectorResult { - let data_request = ironrdp_core::decode::>>(input) + let data_request = ironrdp_core::decode::>>(input) .map_err(ConnectorError::decode) .map(|p| p.0)?; let share_control = ironrdp_core::decode::(data_request.user_data.as_ref()) @@ -230,7 +229,7 @@ fn decode_share_control(input: &[u8]) -> ConnectorResult Result { - use pdu::rdp::headers::{ShareControlPdu, ShareDataPdu}; + use ironrdp_pdu::rdp::headers::{ShareControlPdu, ShareDataPdu}; let share_control = decode_share_control(input).map_err(|_| ())?; diff --git a/crates/ironrdp-connector/src/connection_activation.rs b/crates/ironrdp-connector/src/connection_activation.rs index 9f0f1ed6..999cea24 100644 --- a/crates/ironrdp-connector/src/connection_activation.rs +++ b/crates/ironrdp-connector/src/connection_activation.rs @@ -1,7 +1,7 @@ use core::mem; +use ironrdp_pdu::rdp; use ironrdp_pdu::rdp::capability_sets::CapabilitySet; -use ironrdp_pdu::rdp::{self}; use tracing::{debug, warn}; use crate::{ diff --git a/crates/ironrdp-server/src/helper.rs b/crates/ironrdp-server/src/helper.rs index 38b76fa3..53d6870f 100644 --- a/crates/ironrdp-server/src/helper.rs +++ b/crates/ironrdp-server/src/helper.rs @@ -7,8 +7,7 @@ use anyhow::Context as _; use rustls_pemfile::{certs, pkcs8_private_keys}; use tokio_rustls::rustls::pki_types::pem::PemObject as _; use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer}; -use tokio_rustls::rustls::{self}; -use tokio_rustls::TlsAcceptor; +use tokio_rustls::{rustls, TlsAcceptor}; pub struct TlsIdentityCtx { pub certs: Vec>, diff --git a/crates/ironrdp-tls/src/rustls.rs b/crates/ironrdp-tls/src/rustls.rs index 10c8d8ab..ea3aa3c0 100644 --- a/crates/ironrdp-tls/src/rustls.rs +++ b/crates/ironrdp-tls/src/rustls.rs @@ -1,8 +1,8 @@ use std::io; use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt as _}; +use tokio_rustls::rustls; use tokio_rustls::rustls::pki_types::ServerName; -use tokio_rustls::rustls::{self}; pub type TlsStream = tokio_rustls::client::TlsStream; From ce298d1c193c45fc238f4d294c3686c3fb000ed6 Mon Sep 17 00:00:00 2001 From: Alex Yusiuk <55661041+RRRadicalEdward@users.noreply.github.com> Date: Fri, 3 Oct 2025 12:37:14 +0300 Subject: [PATCH 11/51] refactor: enable `self_named_module_files` clippy lint (#1007) > Checks that module layout uses only mod.rs files. --- Cargo.toml | 2 +- .../src/{windows.rs => windows/mod.rs} | 0 .../basic_output/{bitmap.rs => bitmap/mod.rs} | 0 .../{fast_path.rs => fast_path/mod.rs} | 0 .../{basic_output.rs => basic_output/mod.rs} | 0 .../{pointer.rs => pointer/mod.rs} | 0 .../mod.rs} | 0 .../src/{codecs.rs => codecs/mod.rs} | 0 .../src/codecs/{rfx.rs => rfx/mod.rs} | 0 .../src/{crypto.rs => crypto/mod.rs} | 0 .../gcc/{core_data.rs => core_data/mod.rs} | 0 crates/ironrdp-pdu/src/{gcc.rs => gcc/mod.rs} | 0 .../{bitmap_cache.rs => bitmap_cache/mod.rs} | 0 .../mod.rs} | 0 .../{brush.rs => brush/mod.rs} | 0 .../{general.rs => general/mod.rs} | 0 .../{glyph_cache.rs => glyph_cache/mod.rs} | 0 .../mod.rs} | 0 .../mod.rs} | 0 .../{order.rs => order/mod.rs} | 0 .../{sound.rs => sound/mod.rs} | 0 .../mod.rs} | 0 crates/ironrdp-pdu/src/{rdp.rs => rdp/mod.rs} | 0 .../mod.rs} | 0 .../mod.rs} | 0 .../mod.rs} | 0 .../mod.rs} | 0 .../mod.rs} | 0 .../mod.rs} | 0 .../mod.rs} | 0 .../{session_info.rs => session_info/mod.rs} | 0 .../mod.rs} | 0 .../src/rdp/vc/dvc/{gfx.rs => gfx/mod.rs} | 0 .../src/rdp/vc/{dvc.rs => dvc/mod.rs} | 0 .../ironrdp-pdu/src/rdp/{vc.rs => vc/mod.rs} | 0 .../src/pdu/{esc.rs => esc/mod.rs} | 0 .../tests/graphics/rle.rs | 57 ------------------- .../tests/graphics/rle/mod.rs | 57 +++++++++++++++++++ .../tests/pdu/{pointer.rs => pointer/mod.rs} | 6 +- .../tests/{tests.rs => tests/mod.rs} | 0 40 files changed, 61 insertions(+), 61 deletions(-) rename crates/ironrdp-cliprdr-native/src/{windows.rs => windows/mod.rs} (100%) rename crates/ironrdp-pdu/src/basic_output/{bitmap.rs => bitmap/mod.rs} (100%) rename crates/ironrdp-pdu/src/basic_output/{fast_path.rs => fast_path/mod.rs} (100%) rename crates/ironrdp-pdu/src/{basic_output.rs => basic_output/mod.rs} (100%) rename crates/ironrdp-pdu/src/basic_output/{pointer.rs => pointer/mod.rs} (100%) rename crates/ironrdp-pdu/src/basic_output/{surface_commands.rs => surface_commands/mod.rs} (100%) rename crates/ironrdp-pdu/src/{codecs.rs => codecs/mod.rs} (100%) rename crates/ironrdp-pdu/src/codecs/{rfx.rs => rfx/mod.rs} (100%) rename crates/ironrdp-pdu/src/{crypto.rs => crypto/mod.rs} (100%) rename crates/ironrdp-pdu/src/gcc/{core_data.rs => core_data/mod.rs} (100%) rename crates/ironrdp-pdu/src/{gcc.rs => gcc/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/capability_sets/{bitmap_cache.rs => bitmap_cache/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/capability_sets/{bitmap_codecs.rs => bitmap_codecs/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/capability_sets/{brush.rs => brush/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/capability_sets/{general.rs => general/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/capability_sets/{glyph_cache.rs => glyph_cache/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/{capability_sets.rs => capability_sets/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/capability_sets/{offscreen_bitmap_cache.rs => offscreen_bitmap_cache/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/capability_sets/{order.rs => order/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/capability_sets/{sound.rs => sound/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/capability_sets/{virtual_channel.rs => virtual_channel/mod.rs} (100%) rename crates/ironrdp-pdu/src/{rdp.rs => rdp/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/server_license/{client_new_license_request.rs => client_new_license_request/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/server_license/{client_platform_challenge_response.rs => client_platform_challenge_response/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/server_license/{licensing_error_message.rs => licensing_error_message/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/{server_license.rs => server_license/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/server_license/{server_license_request.rs => server_license_request/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/server_license/{server_platform_challenge.rs => server_platform_challenge/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/server_license/{server_upgrade_license.rs => server_upgrade_license/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/{session_info.rs => session_info/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/{graphics_messages.rs => graphics_messages/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/vc/dvc/{gfx.rs => gfx/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/vc/{dvc.rs => dvc/mod.rs} (100%) rename crates/ironrdp-pdu/src/rdp/{vc.rs => vc/mod.rs} (100%) rename crates/ironrdp-rdpdr/src/pdu/{esc.rs => esc/mod.rs} (100%) delete mode 100644 crates/ironrdp-testsuite-core/tests/graphics/rle.rs create mode 100644 crates/ironrdp-testsuite-core/tests/graphics/rle/mod.rs rename crates/ironrdp-testsuite-core/tests/pdu/{pointer.rs => pointer/mod.rs} (96%) rename crates/ironrdp-testsuite-extra/tests/{tests.rs => tests/mod.rs} (100%) diff --git a/Cargo.toml b/Cargo.toml index d6271f8d..d4ee1c6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -139,7 +139,7 @@ unused_self = "warn" useless_let_if_seq = "warn" string_add = "warn" range_plus_one = "warn" -# TODO: self_named_module_files = "warn" +self_named_module_files = "warn" # TODO: partial_pub_fields = "warn" (should we enable only in pdu crates?) redundant_type_annotations = "warn" unnecessary_self_imports = "warn" diff --git a/crates/ironrdp-cliprdr-native/src/windows.rs b/crates/ironrdp-cliprdr-native/src/windows/mod.rs similarity index 100% rename from crates/ironrdp-cliprdr-native/src/windows.rs rename to crates/ironrdp-cliprdr-native/src/windows/mod.rs diff --git a/crates/ironrdp-pdu/src/basic_output/bitmap.rs b/crates/ironrdp-pdu/src/basic_output/bitmap/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/basic_output/bitmap.rs rename to crates/ironrdp-pdu/src/basic_output/bitmap/mod.rs diff --git a/crates/ironrdp-pdu/src/basic_output/fast_path.rs b/crates/ironrdp-pdu/src/basic_output/fast_path/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/basic_output/fast_path.rs rename to crates/ironrdp-pdu/src/basic_output/fast_path/mod.rs diff --git a/crates/ironrdp-pdu/src/basic_output.rs b/crates/ironrdp-pdu/src/basic_output/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/basic_output.rs rename to crates/ironrdp-pdu/src/basic_output/mod.rs diff --git a/crates/ironrdp-pdu/src/basic_output/pointer.rs b/crates/ironrdp-pdu/src/basic_output/pointer/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/basic_output/pointer.rs rename to crates/ironrdp-pdu/src/basic_output/pointer/mod.rs diff --git a/crates/ironrdp-pdu/src/basic_output/surface_commands.rs b/crates/ironrdp-pdu/src/basic_output/surface_commands/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/basic_output/surface_commands.rs rename to crates/ironrdp-pdu/src/basic_output/surface_commands/mod.rs diff --git a/crates/ironrdp-pdu/src/codecs.rs b/crates/ironrdp-pdu/src/codecs/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/codecs.rs rename to crates/ironrdp-pdu/src/codecs/mod.rs diff --git a/crates/ironrdp-pdu/src/codecs/rfx.rs b/crates/ironrdp-pdu/src/codecs/rfx/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/codecs/rfx.rs rename to crates/ironrdp-pdu/src/codecs/rfx/mod.rs diff --git a/crates/ironrdp-pdu/src/crypto.rs b/crates/ironrdp-pdu/src/crypto/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/crypto.rs rename to crates/ironrdp-pdu/src/crypto/mod.rs diff --git a/crates/ironrdp-pdu/src/gcc/core_data.rs b/crates/ironrdp-pdu/src/gcc/core_data/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/gcc/core_data.rs rename to crates/ironrdp-pdu/src/gcc/core_data/mod.rs diff --git a/crates/ironrdp-pdu/src/gcc.rs b/crates/ironrdp-pdu/src/gcc/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/gcc.rs rename to crates/ironrdp-pdu/src/gcc/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_cache.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_cache/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_cache.rs rename to crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_cache/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs.rs rename to crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/brush.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/brush/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/capability_sets/brush.rs rename to crates/ironrdp-pdu/src/rdp/capability_sets/brush/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/general.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/general/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/capability_sets/general.rs rename to crates/ironrdp-pdu/src/rdp/capability_sets/general/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/glyph_cache.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/glyph_cache/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/capability_sets/glyph_cache.rs rename to crates/ironrdp-pdu/src/rdp/capability_sets/glyph_cache/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/capability_sets.rs rename to crates/ironrdp-pdu/src/rdp/capability_sets/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/offscreen_bitmap_cache.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/offscreen_bitmap_cache/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/capability_sets/offscreen_bitmap_cache.rs rename to crates/ironrdp-pdu/src/rdp/capability_sets/offscreen_bitmap_cache/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/order.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/order/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/capability_sets/order.rs rename to crates/ironrdp-pdu/src/rdp/capability_sets/order/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/sound.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/sound/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/capability_sets/sound.rs rename to crates/ironrdp-pdu/src/rdp/capability_sets/sound/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/virtual_channel.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/virtual_channel/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/capability_sets/virtual_channel.rs rename to crates/ironrdp-pdu/src/rdp/capability_sets/virtual_channel/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp.rs b/crates/ironrdp-pdu/src/rdp/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp.rs rename to crates/ironrdp-pdu/src/rdp/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/server_license/client_new_license_request.rs b/crates/ironrdp-pdu/src/rdp/server_license/client_new_license_request/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/server_license/client_new_license_request.rs rename to crates/ironrdp-pdu/src/rdp/server_license/client_new_license_request/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/server_license/client_platform_challenge_response.rs b/crates/ironrdp-pdu/src/rdp/server_license/client_platform_challenge_response/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/server_license/client_platform_challenge_response.rs rename to crates/ironrdp-pdu/src/rdp/server_license/client_platform_challenge_response/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/server_license/licensing_error_message.rs b/crates/ironrdp-pdu/src/rdp/server_license/licensing_error_message/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/server_license/licensing_error_message.rs rename to crates/ironrdp-pdu/src/rdp/server_license/licensing_error_message/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/server_license.rs b/crates/ironrdp-pdu/src/rdp/server_license/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/server_license.rs rename to crates/ironrdp-pdu/src/rdp/server_license/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/server_license/server_license_request.rs b/crates/ironrdp-pdu/src/rdp/server_license/server_license_request/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/server_license/server_license_request.rs rename to crates/ironrdp-pdu/src/rdp/server_license/server_license_request/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/server_license/server_platform_challenge.rs b/crates/ironrdp-pdu/src/rdp/server_license/server_platform_challenge/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/server_license/server_platform_challenge.rs rename to crates/ironrdp-pdu/src/rdp/server_license/server_platform_challenge/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/server_license/server_upgrade_license.rs b/crates/ironrdp-pdu/src/rdp/server_license/server_upgrade_license/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/server_license/server_upgrade_license.rs rename to crates/ironrdp-pdu/src/rdp/server_license/server_upgrade_license/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/session_info.rs b/crates/ironrdp-pdu/src/rdp/session_info/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/session_info.rs rename to crates/ironrdp-pdu/src/rdp/session_info/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages.rs b/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages.rs rename to crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx.rs b/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/vc/dvc/gfx.rs rename to crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/vc/dvc.rs b/crates/ironrdp-pdu/src/rdp/vc/dvc/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/vc/dvc.rs rename to crates/ironrdp-pdu/src/rdp/vc/dvc/mod.rs diff --git a/crates/ironrdp-pdu/src/rdp/vc.rs b/crates/ironrdp-pdu/src/rdp/vc/mod.rs similarity index 100% rename from crates/ironrdp-pdu/src/rdp/vc.rs rename to crates/ironrdp-pdu/src/rdp/vc/mod.rs diff --git a/crates/ironrdp-rdpdr/src/pdu/esc.rs b/crates/ironrdp-rdpdr/src/pdu/esc/mod.rs similarity index 100% rename from crates/ironrdp-rdpdr/src/pdu/esc.rs rename to crates/ironrdp-rdpdr/src/pdu/esc/mod.rs diff --git a/crates/ironrdp-testsuite-core/tests/graphics/rle.rs b/crates/ironrdp-testsuite-core/tests/graphics/rle.rs deleted file mode 100644 index a13a8cba..00000000 --- a/crates/ironrdp-testsuite-core/tests/graphics/rle.rs +++ /dev/null @@ -1,57 +0,0 @@ -use rstest::rstest; - -/// 64x64 tile samples were generated using rdp-rs crate -#[rstest] -#[case::x27019fd9f222cebce9dfebcddb12bfa0( - include_bytes!("../../test_data/rle/tile-27019fd9f222cebce9dfebcddb12bfa0-compressed.bin"), - include_bytes!("../../test_data/rle/tile-27019fd9f222cebce9dfebcddb12bfa0-decompressed.bin"), -)] -#[case::x284f668a9366a95e45f15b6bf634a633( - include_bytes!("../../test_data/rle/tile-284f668a9366a95e45f15b6bf634a633-compressed.bin"), - include_bytes!("../../test_data/rle/tile-284f668a9366a95e45f15b6bf634a633-decompressed.bin"), -)] -#[case::x28c08e75c82ab598c5ab85d1bfc00253( - include_bytes!("../../test_data/rle/tile-28c08e75c82ab598c5ab85d1bfc00253-compressed.bin"), - include_bytes!("../../test_data/rle/tile-28c08e75c82ab598c5ab85d1bfc00253-decompressed.bin"), -)] -#[case::x2de3f3262a5eeecc3152552c178b782a( - include_bytes!("../../test_data/rle/tile-2de3f3262a5eeecc3152552c178b782a-compressed.bin"), - include_bytes!("../../test_data/rle/tile-2de3f3262a5eeecc3152552c178b782a-decompressed.bin"), -)] -#[case::x3fc8124af9be2fe88b445db60c36eddc( - include_bytes!("../../test_data/rle/tile-3fc8124af9be2fe88b445db60c36eddc-compressed.bin"), - include_bytes!("../../test_data/rle/tile-3fc8124af9be2fe88b445db60c36eddc-decompressed.bin"), -)] -#[case::x4d75aa6a18c435c6230ba739b802a861( - include_bytes!("../../test_data/rle/tile-4d75aa6a18c435c6230ba739b802a861-compressed.bin"), - include_bytes!("../../test_data/rle/tile-4d75aa6a18c435c6230ba739b802a861-decompressed.bin"), -)] -#[case::x8b8ccc77526730d0cd8989901cc031ec( - include_bytes!("../../test_data/rle/tile-8b8ccc77526730d0cd8989901cc031ec-compressed.bin"), - include_bytes!("../../test_data/rle/tile-8b8ccc77526730d0cd8989901cc031ec-decompressed.bin"), -)] -#[case::x94bb5b131eb3bc110905dfcb0f60da79( - include_bytes!("../../test_data/rle/tile-94bb5b131eb3bc110905dfcb0f60da79-compressed.bin"), - include_bytes!("../../test_data/rle/tile-94bb5b131eb3bc110905dfcb0f60da79-decompressed.bin"), -)] -#[case::x9b06660a1da806d2d48ce3f46b45d571( - include_bytes!("../../test_data/rle/tile-9b06660a1da806d2d48ce3f46b45d571-compressed.bin"), - include_bytes!("../../test_data/rle/tile-9b06660a1da806d2d48ce3f46b45d571-decompressed.bin"), -)] -#[case::xa412fbe2b435ac627ce39048aa3d3fb3( - include_bytes!("../../test_data/rle/tile-a412fbe2b435ac627ce39048aa3d3fb3-compressed.bin"), - include_bytes!("../../test_data/rle/tile-a412fbe2b435ac627ce39048aa3d3fb3-decompressed.bin"), -)] -#[case::xaa326e7a536cc8a0420c44bdf4ef8d97( - include_bytes!("../../test_data/rle/tile-aa326e7a536cc8a0420c44bdf4ef8d97-compressed.bin"), - include_bytes!("../../test_data/rle/tile-aa326e7a536cc8a0420c44bdf4ef8d97-decompressed.bin"), -)] -#[case::xfbcefc9af4db651aefd91bcabc8ea9fc( - include_bytes!("../../test_data/rle/tile-fbcefc9af4db651aefd91bcabc8ea9fc-compressed.bin"), - include_bytes!("../../test_data/rle/tile-fbcefc9af4db651aefd91bcabc8ea9fc-decompressed.bin"), -)] -fn decompress_bpp_16(#[case] src: &[u8], #[case] expected: &[u8]) { - let mut out = Vec::new(); - ironrdp_graphics::rle::decompress_16_bpp(src, &mut out, 64, 64).expect("decompress 16 bpp"); - assert_eq!(out, expected); -} diff --git a/crates/ironrdp-testsuite-core/tests/graphics/rle/mod.rs b/crates/ironrdp-testsuite-core/tests/graphics/rle/mod.rs new file mode 100644 index 00000000..6b1f155a --- /dev/null +++ b/crates/ironrdp-testsuite-core/tests/graphics/rle/mod.rs @@ -0,0 +1,57 @@ +use rstest::rstest; + +/// 64x64 tile samples were generated using rdp-rs crate +#[rstest] +#[case::x27019fd9f222cebce9dfebcddb12bfa0( + include_bytes!("../../../test_data/rle/tile-27019fd9f222cebce9dfebcddb12bfa0-compressed.bin"), + include_bytes!("../../../test_data/rle/tile-27019fd9f222cebce9dfebcddb12bfa0-decompressed.bin"), +)] +#[case::x284f668a9366a95e45f15b6bf634a633( + include_bytes!("../../../test_data/rle/tile-284f668a9366a95e45f15b6bf634a633-compressed.bin"), + include_bytes!("../../../test_data/rle/tile-284f668a9366a95e45f15b6bf634a633-decompressed.bin"), +)] +#[case::x28c08e75c82ab598c5ab85d1bfc00253( + include_bytes!("../../../test_data/rle/tile-28c08e75c82ab598c5ab85d1bfc00253-compressed.bin"), + include_bytes!("../../../test_data/rle/tile-28c08e75c82ab598c5ab85d1bfc00253-decompressed.bin"), +)] +#[case::x2de3f3262a5eeecc3152552c178b782a( + include_bytes!("../../../test_data/rle/tile-2de3f3262a5eeecc3152552c178b782a-compressed.bin"), + include_bytes!("../../../test_data/rle/tile-2de3f3262a5eeecc3152552c178b782a-decompressed.bin"), +)] +#[case::x3fc8124af9be2fe88b445db60c36eddc( + include_bytes!("../../../test_data/rle/tile-3fc8124af9be2fe88b445db60c36eddc-compressed.bin"), + include_bytes!("../../../test_data/rle/tile-3fc8124af9be2fe88b445db60c36eddc-decompressed.bin"), +)] +#[case::x4d75aa6a18c435c6230ba739b802a861( + include_bytes!("../../../test_data/rle/tile-4d75aa6a18c435c6230ba739b802a861-compressed.bin"), + include_bytes!("../../../test_data/rle/tile-4d75aa6a18c435c6230ba739b802a861-decompressed.bin"), +)] +#[case::x8b8ccc77526730d0cd8989901cc031ec( + include_bytes!("../../../test_data/rle/tile-8b8ccc77526730d0cd8989901cc031ec-compressed.bin"), + include_bytes!("../../../test_data/rle/tile-8b8ccc77526730d0cd8989901cc031ec-decompressed.bin"), +)] +#[case::x94bb5b131eb3bc110905dfcb0f60da79( + include_bytes!("../../../test_data/rle/tile-94bb5b131eb3bc110905dfcb0f60da79-compressed.bin"), + include_bytes!("../../../test_data/rle/tile-94bb5b131eb3bc110905dfcb0f60da79-decompressed.bin"), +)] +#[case::x9b06660a1da806d2d48ce3f46b45d571( + include_bytes!("../../../test_data/rle/tile-9b06660a1da806d2d48ce3f46b45d571-compressed.bin"), + include_bytes!("../../../test_data/rle/tile-9b06660a1da806d2d48ce3f46b45d571-decompressed.bin"), +)] +#[case::xa412fbe2b435ac627ce39048aa3d3fb3( + include_bytes!("../../../test_data/rle/tile-a412fbe2b435ac627ce39048aa3d3fb3-compressed.bin"), + include_bytes!("../../../test_data/rle/tile-a412fbe2b435ac627ce39048aa3d3fb3-decompressed.bin"), +)] +#[case::xaa326e7a536cc8a0420c44bdf4ef8d97( + include_bytes!("../../../test_data/rle/tile-aa326e7a536cc8a0420c44bdf4ef8d97-compressed.bin"), + include_bytes!("../../../test_data/rle/tile-aa326e7a536cc8a0420c44bdf4ef8d97-decompressed.bin"), +)] +#[case::xfbcefc9af4db651aefd91bcabc8ea9fc( + include_bytes!("../../../test_data/rle/tile-fbcefc9af4db651aefd91bcabc8ea9fc-compressed.bin"), + include_bytes!("../../../test_data/rle/tile-fbcefc9af4db651aefd91bcabc8ea9fc-decompressed.bin"), +)] +fn decompress_bpp_16(#[case] src: &[u8], #[case] expected: &[u8]) { + let mut out = Vec::new(); + ironrdp_graphics::rle::decompress_16_bpp(src, &mut out, 64, 64).expect("decompress 16 bpp"); + assert_eq!(out, expected); +} diff --git a/crates/ironrdp-testsuite-core/tests/pdu/pointer.rs b/crates/ironrdp-testsuite-core/tests/pdu/pointer/mod.rs similarity index 96% rename from crates/ironrdp-testsuite-core/tests/pdu/pointer.rs rename to crates/ironrdp-testsuite-core/tests/pdu/pointer/mod.rs index ad058ff4..5c0d0216 100644 --- a/crates/ironrdp-testsuite-core/tests/pdu/pointer.rs +++ b/crates/ironrdp-testsuite-core/tests/pdu/pointer/mod.rs @@ -38,7 +38,7 @@ fn expect_pointer_png(pointer: &DecodedPointer, expected_file_path: &str) { #[test] fn new_pointer_32bpp() { - let data = include_bytes!("../../test_data/pdu/pointer/new_pointer_32bpp.bin"); + let data = include_bytes!("../../../test_data/pdu/pointer/new_pointer_32bpp.bin"); let mut parsed = ironrdp_core::decode::>(data).unwrap(); let decoded = DecodedPointer::decode_pointer_attribute(&parsed, PointerBitmapTarget::Software).unwrap(); expect_pointer_png(&decoded, "pdu/pointer/new_pointer_32bpp.png"); @@ -69,7 +69,7 @@ fn new_pointer_32bpp() { #[test] fn large_pointer_32bpp() { - let data = include_bytes!("../../test_data/pdu/pointer/large_pointer_32bpp.bin"); + let data = include_bytes!("../../../test_data/pdu/pointer/large_pointer_32bpp.bin"); let mut parsed = ironrdp_core::decode::>(data).unwrap(); let decoded = DecodedPointer::decode_large_pointer_attribute(&parsed, PointerBitmapTarget::Software).unwrap(); expect_pointer_png(&decoded, "pdu/pointer/large_pointer_32bpp.png"); @@ -98,7 +98,7 @@ fn large_pointer_32bpp() { #[test] fn color_pointer_24bpp() { - let data = include_bytes!("../../test_data/pdu/pointer/color_pointer_24bpp.bin"); + let data = include_bytes!("../../../test_data/pdu/pointer/color_pointer_24bpp.bin"); let mut parsed = ironrdp_core::decode::>(data).unwrap(); let decoded = DecodedPointer::decode_color_pointer_attribute(&parsed, PointerBitmapTarget::Software).unwrap(); expect_pointer_png(&decoded, "pdu/pointer/color_pointer_24bpp.png"); diff --git a/crates/ironrdp-testsuite-extra/tests/tests.rs b/crates/ironrdp-testsuite-extra/tests/tests/mod.rs similarity index 100% rename from crates/ironrdp-testsuite-extra/tests/tests.rs rename to crates/ironrdp-testsuite-extra/tests/tests/mod.rs From af8ebdcfa2f08ee897bc207a8f322886c52910bf Mon Sep 17 00:00:00 2001 From: Alex Yusiuk <55661041+RRRadicalEdward@users.noreply.github.com> Date: Mon, 6 Oct 2025 12:19:22 +0300 Subject: [PATCH 12/51] refactor: enable `missing_panics_doc` clippy lint (#1006) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > Checks the doc comments of publicly visible functions that may panic and warns if there is no # Panics section. Co-authored-by: Benoît Cortier <3809077+CBenoit@users.noreply.github.com> --- Cargo.toml | 1 + crates/ironrdp-acceptor/src/connection.rs | 6 ++++++ crates/ironrdp-async/src/connector.rs | 3 +++ crates/ironrdp-async/src/framed.rs | 1 + crates/ironrdp-bench/benches/bench.rs | 2 ++ crates/ironrdp-blocking/src/connector.rs | 3 +++ crates/ironrdp-blocking/src/framed.rs | 1 + .../ironrdp-cliprdr/src/pdu/file_contents.rs | 18 ++++++------------ crates/ironrdp-connector/src/connection.rs | 6 ++++++ crates/ironrdp-dvc/src/lib.rs | 2 ++ .../ironrdp-graphics/src/color_conversion.rs | 4 ++++ crates/ironrdp-graphics/src/lib.rs | 3 +++ crates/ironrdp-graphics/src/rlgr.rs | 2 ++ crates/ironrdp-input/src/lib.rs | 5 +++++ crates/ironrdp-mstsgu/src/lib.rs | 2 +- crates/ironrdp-pdu/src/gcc/network_data.rs | 6 +++++- crates/ironrdp-pdu/src/utils.rs | 13 ++++++++++++- crates/ironrdp-server/src/encoder/rfx.rs | 1 + crates/ironrdp-session/src/fast_path.rs | 6 +++--- ffi/src/dvc/dvc_pipe_proxy_message_queue.rs | 1 + ffi/src/log.rs | 5 +++++ 21 files changed, 73 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d4ee1c6e..a33e63c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -115,6 +115,7 @@ same_name_method = "warn" string_slice = "warn" suspicious_xor_used_as_pow = "warn" unused_result_ok = "warn" +missing_panics_doc = "warn" # == Style, readability == # semicolon_outside_block = "warn" # With semicolon-outside-block-ignore-multiline = true diff --git a/crates/ironrdp-acceptor/src/connection.rs b/crates/ironrdp-acceptor/src/connection.rs index ca27245c..492caab3 100644 --- a/crates/ironrdp-acceptor/src/connection.rs +++ b/crates/ironrdp-acceptor/src/connection.rs @@ -123,6 +123,9 @@ impl Acceptor { } } + /// # Panics + /// + /// Panics if state is not [AcceptorState::SecurityUpgrade]. pub fn mark_security_upgrade_as_done(&mut self) { assert!(self.reached_security_upgrade().is_some()); self.step(&[], &mut WriteBuf::new()).expect("transition to next state"); @@ -133,6 +136,9 @@ impl Acceptor { matches!(self.state, AcceptorState::Credssp { .. }) } + /// # Panics + /// + /// Panics if state is not [AcceptorState::Credssp]. pub fn mark_credssp_as_done(&mut self) { assert!(self.should_perform_credssp()); let res = self.step(&[], &mut WriteBuf::new()).expect("transition to next state"); diff --git a/crates/ironrdp-async/src/connector.rs b/crates/ironrdp-async/src/connector.rs index 5f33c19b..823cfe10 100644 --- a/crates/ironrdp-async/src/connector.rs +++ b/crates/ironrdp-async/src/connector.rs @@ -30,6 +30,9 @@ where Ok(ShouldUpgrade) } +/// # Panics +/// +/// Panics if connector state is not [ClientConnectorState::EnhancedSecurityUpgrade]. pub fn skip_connect_begin(connector: &mut ClientConnector) -> ShouldUpgrade { assert!(connector.should_perform_security_upgrade()); ShouldUpgrade diff --git a/crates/ironrdp-async/src/framed.rs b/crates/ironrdp-async/src/framed.rs index 214682e9..095a8328 100644 --- a/crates/ironrdp-async/src/framed.rs +++ b/crates/ironrdp-async/src/framed.rs @@ -115,6 +115,7 @@ where if self.buf.len() >= length { return Ok(self.buf.split_to(length)); } else { + #[expect(clippy::missing_panics_doc, reason = "unreachable panic (checked integer underflow)")] self.buf .reserve(length.checked_sub(self.buf.len()).expect("length > self.buf.len()")); } diff --git a/crates/ironrdp-bench/benches/bench.rs b/crates/ironrdp-bench/benches/bench.rs index fed6123a..4dc088ad 100644 --- a/crates/ironrdp-bench/benches/bench.rs +++ b/crates/ironrdp-bench/benches/bench.rs @@ -1,3 +1,5 @@ +#![expect(clippy::missing_panics_doc, reason = "panics in benches are allowed")] + use core::num::{NonZeroU16, NonZeroUsize}; use criterion::{criterion_group, criterion_main, Criterion}; diff --git a/crates/ironrdp-blocking/src/connector.rs b/crates/ironrdp-blocking/src/connector.rs index 9375b66a..d601dd98 100644 --- a/crates/ironrdp-blocking/src/connector.rs +++ b/crates/ironrdp-blocking/src/connector.rs @@ -32,6 +32,9 @@ where Ok(ShouldUpgrade) } +/// # Panics +/// +/// Panics if connector state is not [ClientConnectorState::EnhancedSecurityUpgrade]. pub fn skip_connect_begin(connector: &mut ClientConnector) -> ShouldUpgrade { assert!(connector.should_perform_security_upgrade()); ShouldUpgrade diff --git a/crates/ironrdp-blocking/src/framed.rs b/crates/ironrdp-blocking/src/framed.rs index 885fe578..b08a94a4 100644 --- a/crates/ironrdp-blocking/src/framed.rs +++ b/crates/ironrdp-blocking/src/framed.rs @@ -51,6 +51,7 @@ where if self.buf.len() >= length { return Ok(self.buf.split_to(length)); } else { + #[expect(clippy::missing_panics_doc, reason = "unreachable panic (checked underflow)")] self.buf .reserve(length.checked_sub(self.buf.len()).expect("length > self.buf.len()")); } diff --git a/crates/ironrdp-cliprdr/src/pdu/file_contents.rs b/crates/ironrdp-cliprdr/src/pdu/file_contents.rs index 0b329af9..1651b1e1 100644 --- a/crates/ironrdp-cliprdr/src/pdu/file_contents.rs +++ b/crates/ironrdp-cliprdr/src/pdu/file_contents.rs @@ -94,19 +94,13 @@ impl<'a> FileContentsResponse<'a> { /// Read data as u64 size value pub fn data_as_size(&self) -> DecodeResult { - if self.data.len() != 8 { - return Err(invalid_field_err!( - "requestedFileContentsData", - "Invalid data size for u64 size" - )); - } + let chunk = self + .data + .as_ref() + .try_into() + .map_err(|_| invalid_field_err!("requestedFileContentsData", "not enough bytes for u64 size"))?; - Ok(u64::from_le_bytes( - self.data - .as_ref() - .try_into() - .expect("data contains exactly eight u8 elements"), - )) + Ok(u64::from_le_bytes(chunk)) } } diff --git a/crates/ironrdp-connector/src/connection.rs b/crates/ironrdp-connector/src/connection.rs index 231adb1a..1ddd09ee 100644 --- a/crates/ironrdp-connector/src/connection.rs +++ b/crates/ironrdp-connector/src/connection.rs @@ -176,6 +176,9 @@ impl ClientConnector { matches!(self.state, ClientConnectorState::EnhancedSecurityUpgrade { .. }) } + /// # Panics + /// + /// Panics if state is not [ClientConnectorState::EnhancedSecurityUpgrade]. pub fn mark_security_upgrade_as_done(&mut self) { assert!(self.should_perform_security_upgrade()); self.step(&[], &mut WriteBuf::new()).expect("transition to next state"); @@ -186,6 +189,9 @@ impl ClientConnector { matches!(self.state, ClientConnectorState::Credssp { .. }) } + /// # Panics + /// + /// Panics if state is not [ClientConnectorState::Credssp]. pub fn mark_credssp_as_done(&mut self) { assert!(self.should_perform_credssp()); let res = self.step(&[], &mut WriteBuf::new()).expect("transition to next state"); diff --git a/crates/ironrdp-dvc/src/lib.rs b/crates/ironrdp-dvc/src/lib.rs index 48294324..7baa918a 100644 --- a/crates/ironrdp-dvc/src/lib.rs +++ b/crates/ironrdp-dvc/src/lib.rs @@ -73,6 +73,8 @@ pub fn encode_dvc_messages( while off < total_length { let first = off == 0; + + #[expect(clippy::missing_panics_doc, reason = "unreachable panic (checked underflow)")] let remaining_length = total_length.checked_sub(off).expect("never overflow"); let size = core::cmp::min(remaining_length, DrdynvcDataPdu::MAX_DATA_SIZE); let end = off diff --git a/crates/ironrdp-graphics/src/color_conversion.rs b/crates/ironrdp-graphics/src/color_conversion.rs index 46f78ad6..b2554d37 100644 --- a/crates/ironrdp-graphics/src/color_conversion.rs +++ b/crates/ironrdp-graphics/src/color_conversion.rs @@ -40,6 +40,10 @@ pub fn ycbcr_to_rgba(input: YCbCrBuffer<'_>, output: &mut [u8]) -> io::Result<() rdp_yuv444_to_rgba(&planar, output, len).map_err(io::Error::other) } +/// # Panics +/// +/// - Panics if `width` > 64. +/// - Panics if `height` > 64. #[expect(clippy::too_many_arguments)] pub fn to_64x64_ycbcr_tile( input: &[u8], diff --git a/crates/ironrdp-graphics/src/lib.rs b/crates/ironrdp-graphics/src/lib.rs index 192c5bba..03def02e 100644 --- a/crates/ironrdp-graphics/src/lib.rs +++ b/crates/ironrdp-graphics/src/lib.rs @@ -21,6 +21,9 @@ pub mod zgfx; mod utils; +/// # Panics +/// +/// Panics if `input.len()` is not 4096 (64 * 46). pub fn rfx_encode_component( input: &mut [i16], output: &mut [u8], diff --git a/crates/ironrdp-graphics/src/rlgr.rs b/crates/ironrdp-graphics/src/rlgr.rs index c73123ed..31dc09b2 100644 --- a/crates/ironrdp-graphics/src/rlgr.rs +++ b/crates/ironrdp-graphics/src/rlgr.rs @@ -106,9 +106,11 @@ pub fn encode(mode: EntropyAlgorithm, input: &[i16], tile: &mut [u8]) -> Result< k = kp >> LS_GR; } CompressionMode::GolombRice => { + #[expect(clippy::missing_panics_doc, reason = "unreachable panic (prior check)")] let input_first = *input .next() .expect("value is guaranteed to be `Some` due to the prior check"); + match mode { EntropyAlgorithm::Rlgr1 => { let two_ms = get_2magsign(input_first); diff --git a/crates/ironrdp-input/src/lib.rs b/crates/ironrdp-input/src/lib.rs index d40d9e53..64a6a1b0 100644 --- a/crates/ironrdp-input/src/lib.rs +++ b/crates/ironrdp-input/src/lib.rs @@ -86,6 +86,7 @@ impl Scancode { pub fn as_idx(self) -> usize { if self.extended { + #[expect(clippy::missing_panics_doc, reason = "unreachable panic (integer upcast)")] usize::from(self.code).checked_add(256).expect("never overflow") } else { usize::from(self.code) @@ -343,6 +344,7 @@ impl Database { let mut events = SmallVec::new(); for idx in self.mouse_buttons.iter_ones() { + #[expect(clippy::missing_panics_doc, reason = "unreachable panic (checked integer downcast)")] let button = MouseButton::from_idx(idx).expect("in-range index"); let event = match MouseButtonFlags::from(button) { @@ -365,9 +367,12 @@ impl Database { // The keyboard bit array size is 512. for idx in self.keyboard.iter_ones() { let (scancode, extended) = if idx >= 256 { + #[expect(clippy::missing_panics_doc, reason = "unreachable panic (checked integer underflow)")] let extended_code = idx.checked_sub(256).expect("never underflow"); + #[expect(clippy::missing_panics_doc, reason = "unreachable panic (checked integer downcast)")] (u8::try_from(extended_code).expect("always in the range"), true) } else { + #[expect(clippy::missing_panics_doc, reason = "unreachable panic (checked integer downcast)")] (u8::try_from(idx).expect("always in the range"), false) }; diff --git a/crates/ironrdp-mstsgu/src/lib.rs b/crates/ironrdp-mstsgu/src/lib.rs index 92430080..8ed962ad 100644 --- a/crates/ironrdp-mstsgu/src/lib.rs +++ b/crates/ironrdp-mstsgu/src/lib.rs @@ -144,7 +144,7 @@ impl GwClient { .header(hyper::header::SEC_WEBSOCKET_VERSION, "13") .header(hyper::header::SEC_WEBSOCKET_KEY, generate_key()) .body(http_body_util::Empty::::new()) - .expect("Failed to build request"); + .map_err(|e| custom_err!("failed to build request", e))?; let stream = hyper_util::rt::tokio::TokioIo::new(stream); let (mut sender, mut conn) = hyper::client::conn::http1::handshake(stream) diff --git a/crates/ironrdp-pdu/src/gcc/network_data.rs b/crates/ironrdp-pdu/src/gcc/network_data.rs index 33b6d387..2ea457e1 100644 --- a/crates/ironrdp-pdu/src/gcc/network_data.rs +++ b/crates/ironrdp-pdu/src/gcc/network_data.rs @@ -25,6 +25,8 @@ const SERVER_CHANNEL_SIZE: usize = 2; /// is using all the code values from 0 to 255, as such any u8 value is a valid ANSI character. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ChannelName { + /// INVARIANT: A null-terminated 8-byte array. + /// INVARIANT: Contains at most seven ANSI characters. inner: Cow<'static, [u8; Self::SIZE]>, } @@ -79,14 +81,16 @@ impl ChannelName { self.inner.as_ref() } - /// Get a &str if this channel name is a valid ASCII string. pub fn as_str(&self) -> Option<&str> { if self.inner.iter().all(u8::is_ascii) { + #[expect(clippy::missing_panics_doc, reason = "never panics per invariant on self.inner")] let terminator_idx = self .inner .iter() .position(|c| *c == 0) .expect("null-terminated ASCII string"); + + #[expect(clippy::missing_panics_doc, reason = "never panics per invariant on self.inner")] Some(str::from_utf8(&self.inner[..terminator_idx]).expect("ASCII characters")) } else { None diff --git a/crates/ironrdp-pdu/src/utils.rs b/crates/ironrdp-pdu/src/utils.rs index 4326ace3..edfa115d 100644 --- a/crates/ironrdp-pdu/src/utils.rs +++ b/crates/ironrdp-pdu/src/utils.rs @@ -8,8 +8,11 @@ use num_derive::FromPrimitive; use crate::{DecodeResult, EncodeResult}; pub fn split_u64(value: u64) -> (u32, u32) { + #[expect(clippy::missing_panics_doc, reason = "unreachable panic (checked integer downcast)")] let low = u32::try_from(value & 0xFFFF_FFFF).expect("masking with 0xFFFF_FFFF ensures that the value fits into u32"); + + #[expect(clippy::missing_panics_doc, reason = "unreachable panic (checked integer downcast)")] let high = u32::try_from(value >> 32).expect("(u64 >> 32) fits into u32"); (low, high) @@ -31,6 +34,8 @@ pub fn to_utf16_bytes(value: &str) -> Vec { pub fn from_utf16_bytes(mut value: &[u8]) -> String { let mut value_u16 = vec![0x00; value.len() / 2]; + + #[expect(clippy::missing_panics_doc, reason = "unreachable panic (prior constrain)")] value .read_u16_into::(value_u16.as_mut()) .expect("read_u16_into cannot fail at this point"); @@ -106,6 +111,7 @@ pub fn read_string_from_cursor( let str_buffer = &mut slice; let mut u16_buffer = vec![0u16; str_buffer.len() / 2]; + #[expect(clippy::missing_panics_doc, reason = "unreachable panic (prior constrain)")] str_buffer .read_u16_into::(u16_buffer.as_mut()) .expect("BUG: str_buffer is always even for UTF16"); @@ -295,7 +301,12 @@ where ) } -// Utility function that panics on overflow +/// Utility function that panics on overflow +/// +/// # Panics +/// +/// Panics if sum of values overflows. +// FIXME: Is it really something we want to expose from ironrdp-pdu? pub fn strict_sum(values: &[T]) -> T where T: CheckedAdd + Copy + Debug, diff --git a/crates/ironrdp-server/src/encoder/rfx.rs b/crates/ironrdp-server/src/encoder/rfx.rs index fd30d589..198cec1d 100644 --- a/crates/ironrdp-server/src/encoder/rfx.rs +++ b/crates/ironrdp-server/src/encoder/rfx.rs @@ -220,6 +220,7 @@ impl<'a> UpdateEncoder<'a> { } #[cfg(feature = "__bench")] +#[expect(clippy::missing_panics_doc, reason = "panics in benches are allowed")] pub(crate) mod bench { use super::*; diff --git a/crates/ironrdp-session/src/fast_path.rs b/crates/ironrdp-session/src/fast_path.rs index 8dbdc068..06d07cc3 100644 --- a/crates/ironrdp-session/src/fast_path.rs +++ b/crates/ironrdp-session/src/fast_path.rs @@ -221,7 +221,7 @@ impl Processor { let decoded_pointer = Arc::new( DecodedPointer::decode_color_pointer_attribute(&pointer, bitmap_target) - .expect("Failed to decode color pointer attribute"), + .map_err(|e| SessionError::custom("failed to decode color pointer attribute", e))?, ); let _ = self @@ -261,7 +261,7 @@ impl Processor { let decoded_pointer = Arc::new( DecodedPointer::decode_pointer_attribute(&pointer, bitmap_target) - .expect("Failed to decode pointer attribute"), + .map_err(|e| SessionError::custom("failed to decode pointer attribute", e))?, ); let _ = self @@ -279,7 +279,7 @@ impl Processor { let decoded_pointer: Arc = Arc::new( DecodedPointer::decode_large_pointer_attribute(&pointer, bitmap_target) - .expect("Failed to decode large pointer attribute"), + .map_err(|e| SessionError::custom("failed to decode large pointer attribute", e))?, ); let _ = self diff --git a/ffi/src/dvc/dvc_pipe_proxy_message_queue.rs b/ffi/src/dvc/dvc_pipe_proxy_message_queue.rs index a57935b9..3b32c393 100644 --- a/ffi/src/dvc/dvc_pipe_proxy_message_queue.rs +++ b/ffi/src/dvc/dvc_pipe_proxy_message_queue.rs @@ -27,6 +27,7 @@ pub mod ffi { impl DvcPipeProxyMessageQueue { pub fn new(queue_size: u32) -> Box { + #[expect(clippy::missing_panics_doc, reason = "unreachable panic (integer upcast)")] let queue_size = usize::try_from(queue_size).expect("invalid dvc pipe proxy message queue size"); Box::new(DvcPipeProxyMessageQueue(DvcPipeProxyMessageQueueInner::new(queue_size))) diff --git a/ffi/src/log.rs b/ffi/src/log.rs index c1c453df..7f7750a2 100644 --- a/ffi/src/log.rs +++ b/ffi/src/log.rs @@ -14,6 +14,11 @@ pub mod ffi { pub struct Log; impl Log { + /// # Panics + /// + /// - Panics if log directory creation fails. + /// - Panics if tracing initialization fails. + // FIXME: We should return an error instead, because panicking at the FFI boundary is unsafe. pub fn init_with_env() { INIT_LOG.call_once(|| { let log_file = std::env::var(IRONRDP_LOG_PATH).ok(); From 82dbb6460fa576e8ac45a311e859813a289fc9d4 Mon Sep 17 00:00:00 2001 From: Alex Yusiuk <55661041+RRRadicalEdward@users.noreply.github.com> Date: Tue, 7 Oct 2025 12:36:58 +0300 Subject: [PATCH 13/51] refactor(ironrdp-pdu)!: fix `as_conversions` clippy lint warnings (#967) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Benoît CORTIER --- Cargo.toml | 1 + .../src/basic_output/bitmap/mod.rs | 32 +++---- .../src/basic_output/bitmap/rdp6.rs | 4 +- .../src/basic_output/fast_path/mod.rs | 29 +++--- .../src/basic_output/pointer/mod.rs | 39 ++++---- .../src/basic_output/surface_commands/mod.rs | 28 +++--- crates/ironrdp-pdu/src/ber.rs | 63 ++++++++++--- .../src/codecs/rfx/data_messages.rs | 40 +++++---- crates/ironrdp-pdu/src/codecs/rfx/mod.rs | 2 +- crates/ironrdp-pdu/src/crypto/rc4.rs | 10 +-- crates/ironrdp-pdu/src/gcc/cluster_data.rs | 5 +- .../ironrdp-pdu/src/gcc/conference_create.rs | 8 +- crates/ironrdp-pdu/src/gcc/monitor_data.rs | 6 +- crates/ironrdp-pdu/src/input/fast_path.rs | 38 ++++++-- crates/ironrdp-pdu/src/input/mod.rs | 6 +- crates/ironrdp-pdu/src/input/mouse.rs | 18 +++- crates/ironrdp-pdu/src/input/mouse_rel.rs | 2 +- crates/ironrdp-pdu/src/input/mouse_x.rs | 2 +- crates/ironrdp-pdu/src/lib.rs | 8 +- crates/ironrdp-pdu/src/mcs.rs | 90 +++++++++++-------- crates/ironrdp-pdu/src/per.rs | 51 ++++++++--- .../rdp/capability_sets/bitmap_codecs/mod.rs | 8 +- .../src/rdp/capability_sets/mod.rs | 8 +- .../src/rdp/capability_sets/order/mod.rs | 15 +++- .../src/rdp/capability_sets/order/tests.rs | 42 ++++----- crates/ironrdp-pdu/src/rdp/client_info.rs | 25 +++--- .../src/rdp/finalization_messages.rs | 5 +- crates/ironrdp-pdu/src/rdp/headers.rs | 2 +- .../rdp/server_license/client_license_info.rs | 5 +- .../client_new_license_request/mod.rs | 19 ++-- .../client_new_license_request/tests.rs | 12 +-- .../client_platform_challenge_response/mod.rs | 17 ++-- .../test.rs | 20 +++-- .../licensing_error_message/test.rs | 2 +- .../ironrdp-pdu/src/rdp/server_license/mod.rs | 10 ++- .../server_license_request/mod.rs | 5 +- .../server_license_request/tests.rs | 6 +- .../server_platform_challenge/test.rs | 3 +- .../server_upgrade_license/mod.rs | 2 +- .../server_upgrade_license/tests.rs | 12 +-- .../src/rdp/server_license/tests.rs | 2 +- .../src/rdp/session_info/logon_extended.rs | 9 +- .../src/rdp/session_info/logon_info.rs | 2 +- .../dvc/gfx/graphics_messages/avc_messages.rs | 13 +-- .../vc/dvc/gfx/graphics_messages/server.rs | 10 +-- crates/ironrdp-server/src/server.rs | 2 +- crates/ironrdp-session/src/active_stage.rs | 2 +- .../ironrdp-testsuite-core/tests/pdu/input.rs | 4 +- crates/ironrdp-web/src/session.rs | 2 +- 49 files changed, 456 insertions(+), 290 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a33e63c0..14037b6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,6 +93,7 @@ fn_to_numeric_cast_any = "warn" ptr_cast_constness = "warn" # == Correctness == # +#as_conversions = "warn" cast_lossless = "warn" cast_possible_truncation = "warn" cast_possible_wrap = "warn" diff --git a/crates/ironrdp-pdu/src/basic_output/bitmap/mod.rs b/crates/ironrdp-pdu/src/basic_output/bitmap/mod.rs index 52b5163a..f1bd153a 100644 --- a/crates/ironrdp-pdu/src/basic_output/bitmap/mod.rs +++ b/crates/ironrdp-pdu/src/basic_output/bitmap/mod.rs @@ -7,8 +7,8 @@ use core::fmt::{self, Debug}; use bitflags::bitflags; use ironrdp_core::{ - ensure_fixed_part_size, ensure_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, - WriteCursor, + cast_length, ensure_fixed_part_size, ensure_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, + ReadCursor, WriteCursor, }; use crate::geometry::InclusiveRectangle; @@ -41,11 +41,9 @@ impl Encode for BitmapUpdateData<'_> { fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { ensure_size!(in: dst, size: self.size()); - if self.rectangles.len() > u16::MAX as usize { - return Err(invalid_field_err!("numberRectangles", "rectangle count is too big")); - } + let rectangle_count = cast_length!("number of rectangles", self.rectangles.len())?; - Self::encode_header(self.rectangles.len() as u16, dst)?; + Self::encode_header(rectangle_count, dst)?; for bitmap_data in self.rectangles.iter() { bitmap_data.encode(dst)?; @@ -74,10 +72,10 @@ impl<'de> Decode<'de> for BitmapUpdateData<'de> { return Err(invalid_field_err!("updateType", "invalid update type")); } - let rectangles_number = src.read_u16() as usize; - let mut rectangles = Vec::with_capacity(rectangles_number); + let rectangle_count = usize::from(src.read_u16()); + let mut rectangles = Vec::with_capacity(rectangle_count); - for _ in 0..rectangles_number { + for _ in 0..rectangle_count { rectangles.push(BitmapData::decode(src)?); } @@ -111,16 +109,14 @@ impl Encode for BitmapData<'_> { ensure_size!(in: dst, size: self.size()); let encoded_bitmap_data_length = self.encoded_bitmap_data_length(); - if encoded_bitmap_data_length > u16::MAX as usize { - return Err(invalid_field_err!("bitmapLength", "bitmap data length is too big")); - } + let encoded_bitmap_data_length = cast_length!("bitmap data length", encoded_bitmap_data_length)?; self.rectangle.encode(dst)?; dst.write_u16(self.width); dst.write_u16(self.height); dst.write_u16(self.bits_per_pixel); dst.write_u16(self.compression_flags.bits()); - dst.write_u16(encoded_bitmap_data_length as u16); + dst.write_u16(encoded_bitmap_data_length); if let Some(compressed_data_header) = &self.compressed_data_header { compressed_data_header.encode(dst)?; }; @@ -150,25 +146,25 @@ impl<'de> Decode<'de> for BitmapData<'de> { // A 16-bit, unsigned integer. The size in bytes of the data in the bitmapComprHdr // and bitmapDataStream fields. - let encoded_bitmap_data_length = src.read_u16(); + let encoded_bitmap_data_length = usize::from(src.read_u16()); - ensure_size!(in: src, size: encoded_bitmap_data_length as usize); + ensure_size!(in: src, size: encoded_bitmap_data_length); let (compressed_data_header, buffer_length) = if compression_flags.contains(Compression::BITMAP_COMPRESSION) && !compression_flags.contains(Compression::NO_BITMAP_COMPRESSION_HDR) { // Check if encoded_bitmap_data_length is at least CompressedDataHeader::ENCODED_SIZE - if encoded_bitmap_data_length < CompressedDataHeader::ENCODED_SIZE as u16 { + if encoded_bitmap_data_length < CompressedDataHeader::ENCODED_SIZE { return Err(invalid_field_err!( "cbCompEncodedBitmapDataLength", "length is less than CompressedDataHeader::ENCODED_SIZE" )); } - let buffer_length = encoded_bitmap_data_length as usize - CompressedDataHeader::ENCODED_SIZE; + let buffer_length = encoded_bitmap_data_length - CompressedDataHeader::ENCODED_SIZE; (Some(CompressedDataHeader::decode(src)?), buffer_length) } else { - (None, encoded_bitmap_data_length as usize) + (None, encoded_bitmap_data_length) }; let bitmap_data = src.read_slice(buffer_length); diff --git a/crates/ironrdp-pdu/src/basic_output/bitmap/rdp6.rs b/crates/ironrdp-pdu/src/basic_output/bitmap/rdp6.rs index dc8b5a54..bcc1c2e0 100644 --- a/crates/ironrdp-pdu/src/basic_output/bitmap/rdp6.rs +++ b/crates/ironrdp-pdu/src/basic_output/bitmap/rdp6.rs @@ -56,7 +56,7 @@ impl Encode for BitmapStreamHeader { fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { ensure_size!(in: dst, size: self.size()); - let mut header = ((self.enable_rle_compression as u8) << 4) | ((!self.use_alpha as u8) << 5); + let mut header = (u8::from(self.enable_rle_compression) << 4) | (u8::from(!self.use_alpha) << 5); match self.color_plane_definition { ColorPlaneDefinition::Argb => { @@ -68,7 +68,7 @@ impl Encode for BitmapStreamHeader { .. } => { // Add cll and cs flags to header - header |= (color_loss_level & 0x07) | ((use_chroma_subsampling as u8) << 3); + header |= (color_loss_level & 0x07) | (u8::from(use_chroma_subsampling) << 3); } } diff --git a/crates/ironrdp-pdu/src/basic_output/fast_path/mod.rs b/crates/ironrdp-pdu/src/basic_output/fast_path/mod.rs index 20a15dc0..940086ed 100644 --- a/crates/ironrdp-pdu/src/basic_output/fast_path/mod.rs +++ b/crates/ironrdp-pdu/src/basic_output/fast_path/mod.rs @@ -4,8 +4,8 @@ mod tests; use bit_field::BitField as _; use bitflags::bitflags; use ironrdp_core::{ - decode_cursor, ensure_fixed_part_size, ensure_size, invalid_field_err, Decode, DecodeError, DecodeResult, Encode, - EncodeResult, InvalidFieldErr as _, ReadCursor, WriteCursor, + cast_length, decode_cursor, ensure_fixed_part_size, ensure_size, invalid_field_err, Decode, DecodeError, + DecodeResult, Encode, EncodeResult, InvalidFieldErr as _, ReadCursor, WriteCursor, }; use num_derive::FromPrimitive; use num_traits::FromPrimitive as _; @@ -42,7 +42,7 @@ impl FastPathHeader { // it may then be +2 if > 0x7f let len = self.data_length + Self::FIXED_PART_SIZE + 1; - Self::FIXED_PART_SIZE + per::sizeof_length(len as u16) + Self::FIXED_PART_SIZE + per::sizeof_length(len) } } @@ -56,15 +56,13 @@ impl Encode for FastPathHeader { dst.write_u8(header); let length = self.data_length + self.size(); - if length > u16::MAX as usize { - return Err(invalid_field_err!("length", "fastpath PDU length is too big")); - } + let length = cast_length!("length", length)?; if self.forced_long_length { // Preserve same layout for header as received - per::write_long_length(dst, length as u16); + per::write_long_length(dst, length); } else { - per::write_length(dst, length as u16); + per::write_length(dst, length); } Ok(()) @@ -93,14 +91,15 @@ impl<'de> Decode<'de> for FastPathHeader { let (length, sizeof_length) = per::read_length(src).map_err(|e| { DecodeError::invalid_field("", "length", "Invalid encoded fast path PDU length").with_source(e) })?; - if (length as usize) < sizeof_length + Self::FIXED_PART_SIZE { + let length = usize::from(length); + if length < sizeof_length + Self::FIXED_PART_SIZE { return Err(invalid_field_err!( "length", "received fastpath PDU length is smaller than header size" )); } - let data_length = length as usize - sizeof_length - Self::FIXED_PART_SIZE; - // Detect case, when received packet has non-optimal packet length packing + let data_length = length - sizeof_length - Self::FIXED_PART_SIZE; + // Detect case, when received packet has non-optimal packet length packing. let forced_long_length = per::sizeof_length(length) != sizeof_length; Ok(FastPathHeader { @@ -131,9 +130,7 @@ impl Encode for FastPathUpdatePdu<'_> { fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { ensure_size!(in: dst, size: self.size()); - if self.data.len() > u16::MAX as usize { - return Err(invalid_field_err!("data", "fastpath PDU data is too big")); - } + let data_len = cast_length!("data length", self.data.len())?; let mut header = 0u8; header.set_bits(0..4, self.update_code.as_u8()); @@ -148,7 +145,7 @@ impl Encode for FastPathUpdatePdu<'_> { dst.write_u8(compression_flags_with_type); } - dst.write_u16(self.data.len() as u16); + dst.write_u16(data_len); dst.write_slice(self.data); Ok(()) @@ -200,7 +197,7 @@ impl<'de> Decode<'de> for FastPathUpdatePdu<'de> { (None, None) }; - let data_length = src.read_u16() as usize; + let data_length = usize::from(src.read_u16()); ensure_size!(in: src, size: data_length); let data = src.read_slice(data_length); diff --git a/crates/ironrdp-pdu/src/basic_output/pointer/mod.rs b/crates/ironrdp-pdu/src/basic_output/pointer/mod.rs index 285a4243..04bd62c6 100644 --- a/crates/ironrdp-pdu/src/basic_output/pointer/mod.rs +++ b/crates/ironrdp-pdu/src/basic_output/pointer/mod.rs @@ -1,6 +1,6 @@ use ironrdp_core::{ - ensure_fixed_part_size, ensure_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, - WriteCursor, + cast_int, cast_length, ensure_fixed_part_size, ensure_size, invalid_field_err, Decode, DecodeResult, Encode, + EncodeResult, ReadCursor, WriteCursor, }; // Represents `TS_POINT16` described in [MS-RDPBCGR] 2.2.9.1.1.4.1 @@ -69,21 +69,24 @@ macro_rules! check_masks_alignment { ($and_mask:expr, $xor_mask:expr, $pointer_height:expr, $large_ptr:expr) => {{ const AND_MASK_SIZE_FIELD: &str = "lengthAndMask"; const XOR_MASK_SIZE_FIELD: &str = "lengthXorMask"; + const U32_MAX: usize = 0xFFFFFFFF; + + let pointer_height: usize = cast_int!("pointer height", $pointer_height)?; let check_mask = |mask: &[u8], field: &'static str| { if $pointer_height == 0 { return Err(invalid_field_err!(field, "pointer height cannot be zero")); } - if $large_ptr && (mask.len() > u32::MAX as usize) { + if $large_ptr && (mask.len() > U32_MAX) { return Err(invalid_field_err!(field, "pointer mask is too big for u32 size")); } - if !$large_ptr && (mask.len() > u16::MAX as usize) { + if !$large_ptr && (mask.len() > usize::from(u16::MAX)) { return Err(invalid_field_err!(field, "pointer mask is too big for u16 size")); } - if (mask.len() % $pointer_height as usize) != 0 { + if (mask.len() % pointer_height) != 0 { return Err(invalid_field_err!(field, "pointer mask have incomplete scanlines")); } - if (mask.len() / $pointer_height as usize) % 2 != 0 { + if (mask.len() / pointer_height) % 2 != 0 { return Err(invalid_field_err!( field, "pointer mask scanlines should be aligned to 16 bits" @@ -108,8 +111,8 @@ impl Encode for ColorPointerAttribute<'_> { dst.write_u16(self.width); dst.write_u16(self.height); - dst.write_u16(self.and_mask.len() as u16); - dst.write_u16(self.xor_mask.len() as u16); + dst.write_u16(cast_length!("and mask length", self.and_mask.len())?); + dst.write_u16(cast_length!("xor mask length", self.xor_mask.len())?); // Note that masks are written in reverse order. It is not a mistake, that is how the // message is defined in [MS-RDPBCGR] dst.write_slice(self.xor_mask); @@ -135,15 +138,15 @@ impl<'a> Decode<'a> for ColorPointerAttribute<'a> { let hot_spot = Point16::decode(src)?; let width = src.read_u16(); let height = src.read_u16(); - let length_and_mask = src.read_u16(); - let length_xor_mask = src.read_u16(); - // Convert to usize during the addition to prevent overflow and match expected type - let expected_masks_size = (length_and_mask as usize) + (length_xor_mask as usize); + let length_and_mask = usize::from(src.read_u16()); + let length_xor_mask = usize::from(src.read_u16()); + + let expected_masks_size = length_and_mask + length_xor_mask; ensure_size!(in: src, size: expected_masks_size); - let xor_mask = src.read_slice(length_xor_mask as usize); - let and_mask = src.read_slice(length_and_mask as usize); + let xor_mask = src.read_slice(length_xor_mask); + let and_mask = src.read_slice(length_and_mask); check_masks_alignment!(and_mask, xor_mask, height, false)?; @@ -270,8 +273,8 @@ impl Encode for LargePointerAttribute<'_> { dst.write_u16(self.width); dst.write_u16(self.height); - dst.write_u32(self.and_mask.len() as u32); - dst.write_u32(self.xor_mask.len() as u32); + dst.write_u32(cast_length!("and mask length", self.and_mask.len())?); + dst.write_u32(cast_length!("xor mask length", self.xor_mask.len())?); // See comment in `ColorPointerAttribute::encode` about encoding order dst.write_slice(self.xor_mask); dst.write_slice(self.and_mask); @@ -298,8 +301,8 @@ impl<'a> Decode<'a> for LargePointerAttribute<'a> { let width = src.read_u16(); let height = src.read_u16(); // Convert to usize to prevent overflow during addition - let length_and_mask = src.read_u32() as usize; - let length_xor_mask = src.read_u32() as usize; + let length_and_mask = cast_length!("and mask length", src.read_u32())?; + let length_xor_mask = cast_length!("xor mask length", src.read_u32())?; let expected_masks_size = length_and_mask + length_xor_mask; ensure_size!(in: src, size: expected_masks_size); diff --git a/crates/ironrdp-pdu/src/basic_output/surface_commands/mod.rs b/crates/ironrdp-pdu/src/basic_output/surface_commands/mod.rs index a3de9cfc..4f9a6bb4 100644 --- a/crates/ironrdp-pdu/src/basic_output/surface_commands/mod.rs +++ b/crates/ironrdp-pdu/src/basic_output/surface_commands/mod.rs @@ -3,10 +3,10 @@ mod tests; use bitflags::bitflags; use ironrdp_core::{ - ensure_fixed_part_size, ensure_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, - WriteCursor, + cast_length, ensure_fixed_part_size, ensure_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, + ReadCursor, WriteCursor, }; -use num_derive::{FromPrimitive, ToPrimitive}; +use num_derive::FromPrimitive; use num_traits::FromPrimitive as _; use crate::geometry::ExclusiveRectangle; @@ -126,7 +126,7 @@ impl Encode for FrameMarkerPdu { fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { ensure_fixed_part_size!(in: dst); - dst.write_u16(self.frame_action as u16); + dst.write_u16(self.frame_action.as_u16()); dst.write_u32(self.frame_id.unwrap_or(0)); Ok(()) @@ -197,9 +197,7 @@ impl Encode for ExtendedBitmapDataPdu<'_> { fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { ensure_size!(in: dst, size: self.size()); - if self.data.len() > u32::MAX as usize { - return Err(invalid_field_err!("bitmapDataLength", "bitmap data is too big")); - } + let data_len = cast_length!("bitmap data length", self.data.len())?; dst.write_u8(self.bpp); let flags = if self.header.is_some() { @@ -212,7 +210,7 @@ impl Encode for ExtendedBitmapDataPdu<'_> { dst.write_u8(self.codec_id); dst.write_u16(self.width); dst.write_u16(self.height); - dst.write_u32(self.data.len() as u32); + dst.write_u32(data_len); if let Some(header) = &self.header { header.encode(dst)?; } @@ -240,7 +238,7 @@ impl<'de> Decode<'de> for ExtendedBitmapDataPdu<'de> { let codec_id = src.read_u8(); let width = src.read_u16(); let height = src.read_u16(); - let data_length = src.read_u32() as usize; + let data_length = cast_length!("bitmap data length", src.read_u32())?; let expected_remaining_size = if flags.contains(BitmapDataFlags::COMPRESSED_BITMAP_HEADER_PRESENT) { data_length + BitmapDataHeader::ENCODED_SIZE @@ -352,13 +350,23 @@ impl From<&SurfaceCommand<'_>> for SurfaceCommandType { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive)] #[repr(u16)] pub enum FrameAction { Begin = 0x00, End = 0x01, } +impl FrameAction { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] + pub fn as_u16(self) -> u16 { + self as u16 + } +} + bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] struct BitmapDataFlags: u8 { diff --git a/crates/ironrdp-pdu/src/ber.rs b/crates/ironrdp-pdu/src/ber.rs index b01b49ab..bda4e0fc 100644 --- a/crates/ironrdp-pdu/src/ber.rs +++ b/crates/ironrdp-pdu/src/ber.rs @@ -3,13 +3,25 @@ use ironrdp_core::{cast_length, ensure_size, invalid_field_err, ReadCursor, Writ use crate::{DecodeResult, EncodeResult}; #[repr(u8)] +#[derive(Copy, Clone)] pub(crate) enum Pc { Primitive = 0x00, Construct = 0x20, } +impl Pc { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] + fn as_u8(self) -> u8 { + self as u8 + } +} + #[repr(u8)] #[expect(unused)] +#[derive(Copy, Clone)] enum Class { Universal = 0x00, Application = 0x40, @@ -17,8 +29,19 @@ enum Class { Private = 0xC0, } +impl Class { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] + fn as_u8(self) -> u8 { + self as u8 + } +} + #[repr(u8)] #[expect(unused)] +#[derive(Copy, Clone)] enum Tag { Mask = 0x1F, Boolean = 0x01, @@ -30,6 +53,16 @@ enum Tag { Sequence = 0x10, } +impl Tag { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] + fn as_u8(self) -> u8 { + self as u8 + } +} + pub(crate) const SIZEOF_ENUMERATED: usize = 3; pub(crate) const SIZEOF_BOOL: usize = 3; @@ -46,7 +79,7 @@ pub(crate) fn sizeof_sequence_tag(length: u16) -> usize { } pub(crate) fn sizeof_octet_string(length: u16) -> usize { - 1 + sizeof_length(length) + length as usize + 1 + sizeof_length(length) + usize::from(length) } pub(crate) fn sizeof_integer(value: u32) -> usize { @@ -71,7 +104,7 @@ pub(crate) fn read_sequence_tag(stream: &mut ReadCursor<'_>) -> DecodeResult, tagnum: u8, le ensure_size!(in: stream, size: sizeof_application_tag(tagnum, length)); let taglen = if tagnum > 0x1E { - stream.write_u8(Class::Application as u8 | Pc::Construct as u8 | TAG_MASK); + stream.write_u8(Class::Application.as_u8() | Pc::Construct.as_u8() | TAG_MASK); stream.write_u8(tagnum); 2 } else { - stream.write_u8(Class::Application as u8 | Pc::Construct as u8 | (TAG_MASK & tagnum)); + stream.write_u8(Class::Application.as_u8() | Pc::Construct.as_u8() | (TAG_MASK & tagnum)); 1 }; @@ -98,14 +131,14 @@ pub(crate) fn read_application_tag(stream: &mut ReadCursor<'_>, tagnum: u8) -> D let identifier = stream.read_u8(); if tagnum > 0x1E { - if identifier != Class::Application as u8 | Pc::Construct as u8 | TAG_MASK { + if identifier != Class::Application.as_u8() | Pc::Construct.as_u8() | TAG_MASK { return Err(invalid_field_err!("identifier", "invalid application tag identifier")); } ensure_size!(in: stream, size: 1); if stream.read_u8() != tagnum { return Err(invalid_field_err!("tagnum", "invalid application tag identifier")); } - } else if identifier != Class::Application as u8 | Pc::Construct as u8 | (TAG_MASK & tagnum) { + } else if identifier != Class::Application.as_u8() | Pc::Construct.as_u8() | (TAG_MASK & tagnum) { return Err(invalid_field_err!("identifier", "invalid application tag identifier")); } @@ -146,20 +179,22 @@ pub(crate) fn write_integer(stream: &mut WriteCursor<'_>, value: u32) -> EncodeR if value < 0x0000_0080 { write_length(stream, 1)?; ensure_size!(in: stream, size: 1); - stream.write_u8(value as u8); + stream.write_u8(u8::try_from(value).expect("value is guaranteed to fit into u8 due to the prior check")); Ok(3) } else if value < 0x0000_8000 { write_length(stream, 2)?; ensure_size!(in: stream, size: 2); - stream.write_u16_be(value as u16); + stream.write_u16_be(u16::try_from(value).expect("value is guaranteed to fit into u16 due to the prior check")); Ok(4) } else if value < 0x0080_0000 { write_length(stream, 3)?; ensure_size!(in: stream, size: 3); - stream.write_u8((value >> 16) as u8); - stream.write_u16_be((value & 0xFFFF) as u16); + stream.write_u8(u8::try_from(value >> 16).expect("value is guaranteed to fit into u8 due to the prior check")); + stream.write_u16_be( + u16::try_from(value & 0xFFFF).expect("masking with 0xFFFF ensures that the value fits into u16"), + ); Ok(5) } else { @@ -251,7 +286,7 @@ pub(crate) fn read_octet_string_tag(stream: &mut ReadCursor<'_>) -> DecodeResult fn write_universal_tag(stream: &mut WriteCursor<'_>, tag: Tag, pc: Pc) -> EncodeResult { ensure_size!(in: stream, size: 1); - let identifier = Class::Universal as u8 | pc as u8 | (TAG_MASK & tag as u8); + let identifier = Class::Universal.as_u8() | pc.as_u8() | (TAG_MASK & tag.as_u8()); stream.write_u8(identifier); Ok(1) @@ -262,7 +297,7 @@ fn read_universal_tag(stream: &mut ReadCursor<'_>, tag: Tag, pc: Pc) -> DecodeRe let identifier = stream.read_u8(); - if identifier != Class::Universal as u8 | pc as u8 | (TAG_MASK & tag as u8) { + if identifier != Class::Universal.as_u8() | pc.as_u8() | (TAG_MASK & tag.as_u8()) { Err(invalid_field_err!("identifier", "invalid universal tag identifier")) } else { Ok(()) @@ -279,11 +314,11 @@ fn write_length(stream: &mut WriteCursor<'_>, length: u16) -> EncodeResult 0x7F { stream.write_u8(0x80 ^ 0x1); - stream.write_u8(length as u8); + stream.write_u8(u8::try_from(length).expect("length is guaranteed to fit into u8 due to the prior check")); Ok(2) } else { - stream.write_u8(length as u8); + stream.write_u8(u8::try_from(length).expect("length is guaranteed to fit into u8 due to the prior check")); Ok(1) } diff --git a/crates/ironrdp-pdu/src/codecs/rfx/data_messages.rs b/crates/ironrdp-pdu/src/codecs/rfx/data_messages.rs index d85d9cdd..d7da57b2 100644 --- a/crates/ironrdp-pdu/src/codecs/rfx/data_messages.rs +++ b/crates/ironrdp-pdu/src/codecs/rfx/data_messages.rs @@ -304,7 +304,7 @@ impl Encode for TileSetPdu<'_> { dst.write_u16(properties); dst.write_u8(cast_length!("numQuant", self.quants.len())?); - dst.write_u8(TILE_SIZE as u8); + dst.write_u8(u8::try_from(TILE_SIZE).expect("TILE_SIZE value fits into u8")); dst.write_u16(cast_length!("numTiles", self.tiles.len())?); let tiles_data_size = self.tiles.iter().map(|t| Block::Tile(t.clone()).size()).sum::(); @@ -384,7 +384,7 @@ impl<'de> Decode<'de> for TileSetPdu<'de> { } let number_of_tiles = usize::from(src.read_u16()); - let _tiles_data_size = src.read_u32() as usize; + let _tiles_data_size = src.read_u32(); let quants = iter::repeat_with(|| Quant::decode(src)) .take(number_of_quants) @@ -544,24 +544,32 @@ impl Encode for Quant { impl<'de> Decode<'de> for Quant { fn decode(src: &mut ReadCursor<'de>) -> DecodeResult { - #![allow(clippy::similar_names)] // It’s hard to do better than ll3, lh3, etc without going overly verbose. + #![allow( + clippy::similar_names, + reason = "it’s hard to do better than ll3, lh3, etc without going overly verbose" + )] + ensure_fixed_part_size!(in: src); - let level3 = src.read_u16(); - let ll3 = level3.get_bits(0..4) as u8; - let lh3 = level3.get_bits(4..8) as u8; - let hl3 = level3.get_bits(8..12) as u8; - let hh3 = level3.get_bits(12..16) as u8; + let ll3lh3 = src.read_u8(); + let ll3 = ll3lh3.get_bits(0..4); + let lh3 = ll3lh3.get_bits(4..8); - let level2_with_lh1 = src.read_u16(); - let lh2 = level2_with_lh1.get_bits(0..4) as u8; - let hl2 = level2_with_lh1.get_bits(4..8) as u8; - let hh2 = level2_with_lh1.get_bits(8..12) as u8; - let lh1 = level2_with_lh1.get_bits(12..16) as u8; + let hl3hh3 = src.read_u8(); + let hl3 = hl3hh3.get_bits(0..4); + let hh3 = hl3hh3.get_bits(4..8); - let level1 = src.read_u8(); - let hl1 = level1.get_bits(0..4); - let hh1 = level1.get_bits(4..8); + let lh2hl2 = src.read_u8(); + let lh2 = lh2hl2.get_bits(0..4); + let hl2 = lh2hl2.get_bits(4..8); + + let hh2lh1 = src.read_u8(); + let hh2 = hh2lh1.get_bits(0..4); + let lh1 = hh2lh1.get_bits(4..8); + + let hl1hh1 = src.read_u8(); + let hl1 = hl1hh1.get_bits(0..4); + let hh1 = hl1hh1.get_bits(4..8); Ok(Self { ll3, diff --git a/crates/ironrdp-pdu/src/codecs/rfx/mod.rs b/crates/ironrdp-pdu/src/codecs/rfx/mod.rs index 2d9a2f02..992cdbf3 100644 --- a/crates/ironrdp-pdu/src/codecs/rfx/mod.rs +++ b/crates/ironrdp-pdu/src/codecs/rfx/mod.rs @@ -208,7 +208,7 @@ impl<'de> Decode<'de> for BlockHeader { let ty = src.read_u16(); let ty = BlockType::from_u16(ty).ok_or_else(|| invalid_field_err!("blockType", "Invalid block type"))?; - let data_length = src.read_u32() as usize; + let data_length: usize = cast_length!("block length", src.read_u32())?; data_length .checked_sub(Self::FIXED_PART_SIZE) .ok_or_else(|| invalid_field_err!("blockLen", "Invalid block length"))?; diff --git a/crates/ironrdp-pdu/src/crypto/rc4.rs b/crates/ironrdp-pdu/src/crypto/rc4.rs index 3123e197..abcafcf0 100644 --- a/crates/ironrdp-pdu/src/crypto/rc4.rs +++ b/crates/ironrdp-pdu/src/crypto/rc4.rs @@ -11,12 +11,12 @@ impl Rc4 { pub(crate) fn new(key: &[u8]) -> Self { // key scheduling let mut state = State::default(); - for (i, item) in state.iter_mut().enumerate().take(256) { - *item = i as u8; + for (i, item) in (0..=255).zip(state.iter_mut()) { + *item = i; } let mut j = 0usize; for i in 0..256 { - j = (j + state[i] as usize + key[i % key.len()] as usize) % 256; + j = (j + usize::from(state[i]) + usize::from(key[i % key.len()])) % 256; state.swap(i, j); } @@ -28,9 +28,9 @@ impl Rc4 { let mut output = Vec::with_capacity(message.len()); while output.capacity() > output.len() { self.i = (self.i + 1) % 256; - self.j = (self.j + self.state[self.i] as usize) % 256; + self.j = (self.j + usize::from(self.state[self.i])) % 256; self.state.swap(self.i, self.j); - let idx_k = (self.state[self.i] as usize + self.state[self.j] as usize) % 256; + let idx_k = (usize::from(self.state[self.i]) + usize::from(self.state[self.j])) % 256; let k = self.state[idx_k]; let idx_msg = output.len(); output.push(k ^ message[idx_msg]); diff --git a/crates/ironrdp-pdu/src/gcc/cluster_data.rs b/crates/ironrdp-pdu/src/gcc/cluster_data.rs index bda5357d..56753a12 100644 --- a/crates/ironrdp-pdu/src/gcc/cluster_data.rs +++ b/crates/ironrdp-pdu/src/gcc/cluster_data.rs @@ -56,9 +56,8 @@ impl<'de> Decode<'de> for ClientClusterData { let flags = RedirectionFlags::from_bits(flags_with_version & !REDIRECTION_VERSION_MASK) .ok_or_else(|| invalid_field_err!("flags", "invalid redirection flags"))?; - let redirection_version = - RedirectionVersion::from_u8(((flags_with_version & REDIRECTION_VERSION_MASK) >> 2) as u8) - .ok_or_else(|| invalid_field_err!("redirVersion", "invalid redirection version"))?; + let redirection_version = RedirectionVersion::from_u32((flags_with_version & REDIRECTION_VERSION_MASK) >> 2) + .ok_or_else(|| invalid_field_err!("redirVersion", "invalid redirection version"))?; Ok(Self { flags, diff --git a/crates/ironrdp-pdu/src/gcc/conference_create.rs b/crates/ironrdp-pdu/src/gcc/conference_create.rs index 9c5e3ee4..4d3cce3c 100644 --- a/crates/ironrdp-pdu/src/gcc/conference_create.rs +++ b/crates/ironrdp-pdu/src/gcc/conference_create.rs @@ -114,9 +114,9 @@ impl Encode for ConferenceCreateRequest { per::CHOICE_SIZE + CONFERENCE_REQUEST_OBJECT_ID.len() - + per::sizeof_length(req_length) + + per::sizeof_length(usize::from(req_length)) + CONFERENCE_REQUEST_CONNECT_PDU_SIZE - + per::sizeof_length(length) + + per::sizeof_length(usize::from(length)) + gcc_blocks_buffer_length } } @@ -286,9 +286,9 @@ impl Encode for ConferenceCreateResponse { per::CHOICE_SIZE + CONFERENCE_REQUEST_OBJECT_ID.len() - + per::sizeof_length(req_length) + + per::sizeof_length(usize::from(req_length)) + CONFERENCE_RESPONSE_CONNECT_PDU_SIZE - + per::sizeof_length(length) + + per::sizeof_length(usize::from(length)) + gcc_blocks_buffer_length } } diff --git a/crates/ironrdp-pdu/src/gcc/monitor_data.rs b/crates/ironrdp-pdu/src/gcc/monitor_data.rs index f6cb021b..643d3421 100644 --- a/crates/ironrdp-pdu/src/gcc/monitor_data.rs +++ b/crates/ironrdp-pdu/src/gcc/monitor_data.rs @@ -49,13 +49,13 @@ impl<'de> Decode<'de> for ClientMonitorData { ensure_fixed_part_size!(in: src); let _flags = src.read_u32(); // is unused - let monitor_count = src.read_u32(); + let monitor_count = cast_length!("number of monitors", src.read_u32())?; - if monitor_count > MONITOR_COUNT_MAX as u32 { + if monitor_count > MONITOR_COUNT_MAX { return Err(invalid_field_err!("nMonitors", "too many monitors")); } - let mut monitors = Vec::with_capacity(monitor_count as usize); + let mut monitors = Vec::with_capacity(monitor_count); for _ in 0..monitor_count { monitors.push(Monitor::decode(src)?); } diff --git a/crates/ironrdp-pdu/src/input/fast_path.rs b/crates/ironrdp-pdu/src/input/fast_path.rs index 0f4e7d1b..abbe7816 100644 --- a/crates/ironrdp-pdu/src/input/fast_path.rs +++ b/crates/ironrdp-pdu/src/input/fast_path.rs @@ -51,9 +51,7 @@ impl Encode for FastPathInputHeader { fn size(&self) -> usize { let num_events_length = if self.num_events < 16 { 0 } else { 1 }; - Self::FIXED_PART_SIZE - + per::sizeof_length(self.data_length as u16 + num_events_length as u16 + 1) - + num_events_length + Self::FIXED_PART_SIZE + per::sizeof_length(self.data_length + num_events_length + 1) + num_events_length } } @@ -78,7 +76,7 @@ impl<'de> Decode<'de> for FastPathInputHeader { 0 }; - let data_length = length as usize - sizeof_length - 1 - num_events_length; + let data_length = usize::from(length) - sizeof_length - 1 - num_events_length; Ok(FastPathInputHeader { flags, @@ -110,7 +108,7 @@ impl FastpathInputEventType { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum FastPathInputEvent { KeyboardEvent(KeyboardFlags, u8), UnicodeKeyboardEvent(KeyboardFlags, u16), @@ -255,10 +253,31 @@ bitflags! { } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct FastPathInput(pub Vec); +pub struct FastPathInput( + /// INVARIANT: (1..=255).contains(len()) = at least one, and at most 255 elements. + Vec, +); impl FastPathInput { const NAME: &'static str = "FastPathInput"; + + pub fn new(input_events: Vec) -> DecodeResult { + // Ensure the invariant on `input_events.len()` is respected. + if !(1..=255usize).contains(&input_events.len()) { + return Err(invalid_field_err!("nEvents", "invalid number of input events")); + } + + Ok(Self(input_events)) + } + + pub fn single(input_event: FastPathInputEvent) -> Self { + // A single element upholds the invariant. + Self(vec![input_event]) + } + + pub fn input_events(&self) -> &[FastPathInputEvent] { + &self.0 + } } impl Encode for FastPathInput { @@ -271,7 +290,7 @@ impl Encode for FastPathInput { let data_length = self.0.iter().map(Encode::size).sum::(); let header = FastPathInputHeader { - num_events: self.0.len() as u8, + num_events: u8::try_from(self.0.len()).expect("per invariant (1..=255).contains(num_events.len())"), flags: EncryptionFlags::empty(), data_length, }; @@ -291,7 +310,8 @@ impl Encode for FastPathInput { fn size(&self) -> usize { let data_length = self.0.iter().map(Encode::size).sum::(); let header = FastPathInputHeader { - num_events: self.0.len() as u8, + num_events: u8::try_from(self.0.len()) + .expect("INVARIANT: num_events is within the range of 1 to 255, inclusive"), flags: EncryptionFlags::empty(), data_length, }; @@ -306,6 +326,6 @@ impl<'de> Decode<'de> for FastPathInput { .take(usize::from(header.num_events)) .collect::, _>>()?; - Ok(Self(events)) + Self::new(events) } } diff --git a/crates/ironrdp-pdu/src/input/mod.rs b/crates/ironrdp-pdu/src/input/mod.rs index 0cdafdd6..a85b5195 100644 --- a/crates/ironrdp-pdu/src/input/mod.rs +++ b/crates/ironrdp-pdu/src/input/mod.rs @@ -1,8 +1,8 @@ use std::io; use ironrdp_core::{ - ensure_fixed_part_size, ensure_size, invalid_field_err, read_padding, write_padding, Decode, DecodeResult, Encode, - EncodeResult, ReadCursor, WriteCursor, + cast_length, ensure_fixed_part_size, ensure_size, invalid_field_err, read_padding, write_padding, Decode, + DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor, }; use num_derive::FromPrimitive; use num_traits::FromPrimitive as _; @@ -38,7 +38,7 @@ impl Encode for InputEventPdu { fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { ensure_size!(in: dst, size: self.size()); - dst.write_u16(self.0.len() as u16); + dst.write_u16(cast_length!("input events count", self.0.len())?); write_padding!(dst, 2); for event in self.0.iter() { diff --git a/crates/ironrdp-pdu/src/input/mouse.rs b/crates/ironrdp-pdu/src/input/mouse.rs index 6f8fc159..3125739e 100644 --- a/crates/ironrdp-pdu/src/input/mouse.rs +++ b/crates/ironrdp-pdu/src/input/mouse.rs @@ -1,7 +1,7 @@ use bitflags::bitflags; use ironrdp_core::{ensure_fixed_part_size, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor}; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct MousePdu { pub flags: PointerFlags, pub number_of_wheel_rotation_units: i16, @@ -25,7 +25,14 @@ impl Encode for MousePdu { PointerFlags::empty().bits() }; - let wheel_rotations_bits = u16::from(self.number_of_wheel_rotation_units as u8); // truncate + #[expect( + clippy::as_conversions, + clippy::cast_sign_loss, + clippy::cast_possible_truncation, + reason = "truncation intended" + )] + let truncated_wheel_rotation_units = self.number_of_wheel_rotation_units as u8; + let wheel_rotations_bits = u16::from(truncated_wheel_rotation_units); let flags = self.flags.bits() | wheel_negative_bit | wheel_rotations_bits; @@ -53,7 +60,12 @@ impl<'de> Decode<'de> for MousePdu { let flags = PointerFlags::from_bits_truncate(flags_raw); - let wheel_rotations_bits = flags_raw as u8; // truncate + #[expect( + clippy::as_conversions, + clippy::cast_possible_truncation, + reason = "truncation intended" + )] + let wheel_rotations_bits = flags_raw as u8; let number_of_wheel_rotation_units = if flags.contains(PointerFlags::WHEEL_NEGATIVE) { -i16::from(wheel_rotations_bits) diff --git a/crates/ironrdp-pdu/src/input/mouse_rel.rs b/crates/ironrdp-pdu/src/input/mouse_rel.rs index 47f1068d..a1812519 100644 --- a/crates/ironrdp-pdu/src/input/mouse_rel.rs +++ b/crates/ironrdp-pdu/src/input/mouse_rel.rs @@ -1,7 +1,7 @@ use bitflags::bitflags; use ironrdp_core::{ensure_fixed_part_size, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor}; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct MouseRelPdu { pub flags: PointerRelFlags, pub x_delta: i16, diff --git a/crates/ironrdp-pdu/src/input/mouse_x.rs b/crates/ironrdp-pdu/src/input/mouse_x.rs index 10937cf8..500e97c7 100644 --- a/crates/ironrdp-pdu/src/input/mouse_x.rs +++ b/crates/ironrdp-pdu/src/input/mouse_x.rs @@ -1,7 +1,7 @@ use bitflags::bitflags; use ironrdp_core::{ensure_fixed_part_size, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor}; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct MouseXPdu { pub flags: PointerXFlags, pub x_position: u16, diff --git a/crates/ironrdp-pdu/src/lib.rs b/crates/ironrdp-pdu/src/lib.rs index c6e2dadc..6d2a387d 100644 --- a/crates/ironrdp-pdu/src/lib.rs +++ b/crates/ironrdp-pdu/src/lib.rs @@ -1,10 +1,6 @@ #![cfg_attr(doc, doc = include_str!("../README.md"))] #![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")] #![allow(clippy::arithmetic_side_effects)] // FIXME: remove -#![allow(clippy::cast_lossless)] // FIXME: remove -#![allow(clippy::cast_possible_truncation)] // FIXME: remove -#![allow(clippy::cast_possible_wrap)] // FIXME: remove -#![allow(clippy::cast_sign_loss)] // FIXME: remove use core::fmt; @@ -104,6 +100,10 @@ impl Action { } } + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] pub fn as_u8(self) -> u8 { self as u8 } diff --git a/crates/ironrdp-pdu/src/mcs.rs b/crates/ironrdp-pdu/src/mcs.rs index ca2c8ff4..41ba30a6 100644 --- a/crates/ironrdp-pdu/src/mcs.rs +++ b/crates/ironrdp-pdu/src/mcs.rs @@ -232,6 +232,10 @@ impl DomainMcsPdu { } } + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] fn as_u8(self) -> u8 { self as u8 } @@ -622,11 +626,7 @@ impl<'de> McsPdu<'de> for SendDataRequest<'de> { } fn mcs_size(&self) -> usize { - per::CHOICE_SIZE - + per::U16_SIZE * 2 - + 1 - + per::sizeof_length(u16::try_from(self.user_data.len()).unwrap_or(u16::MAX)) - + self.user_data.len() + per::CHOICE_SIZE + per::U16_SIZE * 2 + 1 + per::sizeof_length(self.user_data.len()) + self.user_data.len() } } @@ -703,11 +703,7 @@ impl<'de> McsPdu<'de> for SendDataIndication<'de> { } fn mcs_size(&self) -> usize { - per::CHOICE_SIZE - + per::U16_SIZE * 2 - + 1 - + per::sizeof_length(u16::try_from(self.user_data.len()).unwrap_or(u16::MAX)) - + self.user_data.len() + per::CHOICE_SIZE + per::U16_SIZE * 2 + 1 + per::sizeof_length(self.user_data.len()) + self.user_data.len() } } @@ -723,7 +719,11 @@ pub enum DisconnectReason { } impl DisconnectReason { - pub fn as_u8(self) -> u8 { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] + fn as_u8(self) -> u8 { self as u8 } @@ -944,7 +944,7 @@ mod legacy { use std::io; - use ironrdp_core::{Decode, DecodeResult, Encode}; + use ironrdp_core::{cast_int, Decode, DecodeResult, Encode}; use thiserror::Error; use super::{ @@ -977,11 +977,15 @@ mod legacy { const NAME: &'static str = "ConnectInitial"; fn fields_buffer_ber_length(&self) -> usize { - ber::sizeof_octet_string(self.calling_domain_selector.len() as u16) - + ber::sizeof_octet_string(self.called_domain_selector.len() as u16) - + ber::SIZEOF_BOOL - + (self.target_parameters.size() + self.min_parameters.size() + self.max_parameters.size()) - + ber::sizeof_octet_string(self.conference_create_request.size() as u16) + // Can't rewrite in `as`-less way, because it's used in `Encode::size` which doesn't return an error. + #[expect(clippy::cast_possible_truncation, clippy::as_conversions)] + { + ber::sizeof_octet_string(self.calling_domain_selector.len() as u16) + + ber::sizeof_octet_string(self.called_domain_selector.len() as u16) + + ber::SIZEOF_BOOL + + (self.target_parameters.size() + self.min_parameters.size() + self.max_parameters.size()) + + ber::sizeof_octet_string(self.conference_create_request.size() as u16) + } } } @@ -989,7 +993,8 @@ mod legacy { fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { ensure_size!(in: dst, size: self.size()); - ber::write_application_tag(dst, MCS_TYPE_CONNECT_INITIAL, self.fields_buffer_ber_length() as u16)?; + let field_buffer_ber_length = cast_length!("field_buffer_ber_length", self.fields_buffer_ber_length())?; + ber::write_application_tag(dst, MCS_TYPE_CONNECT_INITIAL, field_buffer_ber_length)?; ber::write_octet_string(dst, self.calling_domain_selector.as_ref())?; ber::write_octet_string(dst, self.called_domain_selector.as_ref())?; ber::write_bool(dst, self.upward_flag)?; @@ -1008,9 +1013,12 @@ mod legacy { fn size(&self) -> usize { let fields_buffer_ber_length = self.fields_buffer_ber_length(); + // Can't rewrite in `as`-less way, because it's used in `Encode::size` which doesn't return an error. + #[expect(clippy::cast_possible_truncation, clippy::as_conversions)] + let fields_buffer_ber_length_u16 = fields_buffer_ber_length as u16; fields_buffer_ber_length - + ber::sizeof_application_tag(MCS_TYPE_CONNECT_INITIAL, fields_buffer_ber_length as u16) + + ber::sizeof_application_tag(MCS_TYPE_CONNECT_INITIAL, fields_buffer_ber_length_u16) } } @@ -1042,10 +1050,14 @@ mod legacy { const NAME: &'static str = "ConnectResponse"; fn fields_buffer_ber_length(&self) -> usize { - ber::SIZEOF_ENUMERATED - + ber::sizeof_integer(self.called_connect_id) - + self.domain_parameters.size() - + ber::sizeof_octet_string(self.conference_create_response.size() as u16) + // Can't rewrite in `as`-less way, because it's used in `Encode::size` which doesn't return an error. + #[expect(clippy::cast_possible_truncation, clippy::as_conversions)] + { + ber::SIZEOF_ENUMERATED + + ber::sizeof_integer(self.called_connect_id) + + self.domain_parameters.size() + + ber::sizeof_octet_string(self.conference_create_response.size() as u16) + } } } @@ -1053,7 +1065,8 @@ mod legacy { fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { ensure_size!(in: dst, size: self.size()); - ber::write_application_tag(dst, MCS_TYPE_CONNECT_RESPONSE, self.fields_buffer_ber_length() as u16)?; + let field_buffer_ber_length = cast_length!("field_buffer_ber_length", self.fields_buffer_ber_length())?; + ber::write_application_tag(dst, MCS_TYPE_CONNECT_RESPONSE, field_buffer_ber_length)?; ber::write_enumerated(dst, 0)?; ber::write_integer(dst, self.called_connect_id)?; self.domain_parameters.encode(dst)?; @@ -1069,9 +1082,11 @@ mod legacy { fn size(&self) -> usize { let fields_buffer_ber_length = self.fields_buffer_ber_length(); - + // Can't rewrite in `as`-less way, because it's used in `Encode::size` which doesn't return an error. + #[expect(clippy::cast_possible_truncation, clippy::as_conversions)] + let fields_buffer_ber_length_u16 = fields_buffer_ber_length as u16; fields_buffer_ber_length - + ber::sizeof_application_tag(MCS_TYPE_CONNECT_RESPONSE, fields_buffer_ber_length as u16) + + ber::sizeof_application_tag(MCS_TYPE_CONNECT_RESPONSE, fields_buffer_ber_length_u16) } } @@ -1079,7 +1094,7 @@ mod legacy { fn decode(src: &mut ReadCursor<'de>) -> DecodeResult { ber::read_application_tag(src, MCS_TYPE_CONNECT_RESPONSE)?; ber::read_enumerated(src, RESULT_ENUM_LENGTH)?; - let called_connect_id = ber::read_integer(src)? as u32; + let called_connect_id = cast_int!("called_connect_id", ber::read_integer(src)?)?; let domain_parameters = DomainParameters::decode(src)?; let _user_data_buffer_length = ber::read_octet_string_tag(src)?; let conference_create_response = ConferenceCreateResponse::decode(src)?; @@ -1131,22 +1146,25 @@ mod legacy { fn size(&self) -> usize { let fields_buffer_ber_length = self.fields_buffer_ber_length(); + // Can't rewrite in `as`-less way, because it's used in `Encode::size` which doesn't return an error. + #[expect(clippy::cast_possible_truncation, clippy::as_conversions)] + let fields_buffer_ber_length_u16 = fields_buffer_ber_length as u16; // FIXME: maybe size should return PduResult... - fields_buffer_ber_length + ber::sizeof_sequence_tag(fields_buffer_ber_length as u16) + fields_buffer_ber_length + ber::sizeof_sequence_tag(fields_buffer_ber_length_u16) } } impl<'de> Decode<'de> for DomainParameters { fn decode(src: &mut ReadCursor<'de>) -> DecodeResult { ber::read_sequence_tag(src)?; - let max_channel_ids = ber::read_integer(src)? as u32; - let max_user_ids = ber::read_integer(src)? as u32; - let max_token_ids = ber::read_integer(src)? as u32; - let num_priorities = ber::read_integer(src)? as u32; - let min_throughput = ber::read_integer(src)? as u32; - let max_height = ber::read_integer(src)? as u32; - let max_mcs_pdu_size = ber::read_integer(src)? as u32; - let protocol_version = ber::read_integer(src)? as u32; + let max_channel_ids = cast_int!("max_channel_ids", ber::read_integer(src)?)?; + let max_user_ids = cast_int!("max_user_ids", ber::read_integer(src)?)?; + let max_token_ids = cast_int!("max_token_ids", ber::read_integer(src)?)?; + let num_priorities = cast_int!("num_priorities", ber::read_integer(src)?)?; + let min_throughput = cast_int!("min_throughput", ber::read_integer(src)?)?; + let max_height = cast_int!("max_height", ber::read_integer(src)?)?; + let max_mcs_pdu_size = cast_int!("max_mcs_pdu_size", ber::read_integer(src)?)?; + let protocol_version = cast_int!("protocol_version", ber::read_integer(src)?)?; Ok(Self { max_channel_ids, diff --git a/crates/ironrdp-pdu/src/per.rs b/crates/ironrdp-pdu/src/per.rs index a1d2e002..5538a6e2 100644 --- a/crates/ironrdp-pdu/src/per.rs +++ b/crates/ironrdp-pdu/src/per.rs @@ -114,7 +114,7 @@ pub(crate) fn write_long_length(dst: &mut WriteCursor<'_>, length: u16) { dst.write_u16_be(length | 0x8000) } -pub(crate) fn sizeof_length(length: u16) -> usize { +pub(crate) fn sizeof_length(length: usize) -> usize { if length > 0x7f { 2 } else { @@ -243,7 +243,10 @@ pub(crate) fn read_object_id(src: &mut ReadCursor<'_>) -> Result<[u8; OBJECT_ID_ } pub(crate) fn write_object_id(dst: &mut WriteCursor<'_>, object_ids: [u8; OBJECT_ID_SIZE]) { - write_length(dst, OBJECT_ID_SIZE as u16 - 1); + write_length( + dst, + u16::try_from(OBJECT_ID_SIZE).expect("OBJECT_ID_SIZE fits into u16") - 1, + ); let first_two_tuples = object_ids[0] * 40 + object_ids[1]; dst.write_u8(first_two_tuples); @@ -338,8 +341,8 @@ pub(crate) mod legacy { Ok(2) } - pub(crate) fn write_short_length(mut stream: impl io::Write, length: u16) -> io::Result { - stream.write_u8(length as u8)?; + pub(crate) fn write_short_length(mut stream: impl io::Write, length: u8) -> io::Result { + stream.write_u8(length)?; Ok(1) } @@ -347,6 +350,12 @@ pub(crate) mod legacy { if length > 0x7f { write_long_length(stream, length) } else { + #[expect( + clippy::as_conversions, + clippy::cast_possible_truncation, + reason = "cast is valid due to prior check" + )] + let length = length as u8; write_short_length(stream, length) } } @@ -413,12 +422,25 @@ pub(crate) mod legacy { pub(crate) fn write_u32(mut stream: impl io::Write, value: u32) -> io::Result { if value <= 0xff { let size = write_length(&mut stream, 1)?; - stream.write_u8(value as u8)?; + + #[expect( + clippy::as_conversions, + clippy::cast_possible_truncation, + reason = "cast is valid due to prior check" + )] + let value = value as u8; + stream.write_u8(value)?; Ok(size + 1) } else if value <= 0xffff { let size = write_length(&mut stream, 2)?; - stream.write_u16::(value as u16)?; + #[expect( + clippy::as_conversions, + clippy::cast_possible_truncation, + reason = "cast is valid due to prior check" + )] + let value = value as u16; + stream.write_u16::(value)?; Ok(size + 2) } else { @@ -488,7 +510,10 @@ pub(crate) mod legacy { } pub(crate) fn write_object_id(mut stream: impl io::Write, object_ids: [u8; OBJECT_ID_SIZE]) -> io::Result { - let size = write_length(&mut stream, OBJECT_ID_SIZE as u16 - 1)?; + let object_oid_size: u16 = OBJECT_ID_SIZE + .try_into() + .expect("OBJECT_ID_SIZE is known to fit into u16"); + let size = write_length(&mut stream, object_oid_size - 1)?; let first_two_tuples = object_ids[0] * 40 + object_ids[1]; stream.write_u8(first_two_tuples)?; @@ -503,7 +528,7 @@ pub(crate) mod legacy { pub(crate) fn read_octet_string(mut stream: impl io::Read, min: usize) -> io::Result> { let (read_length, _) = read_length(&mut stream)?; - let mut read_octet_string = vec![0; min + read_length as usize]; + let mut read_octet_string = vec![0; min + usize::from(read_length)]; stream.read_exact(read_octet_string.as_mut())?; Ok(read_octet_string) @@ -516,7 +541,9 @@ pub(crate) mod legacy { min }; - let size = write_length(&mut stream, length as u16)?; + let length = u16::try_from(length) + .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid octet string length"))?; + let size = write_length(&mut stream, length)?; stream.write_all(octet_string)?; Ok(size + octet_string.len()) @@ -527,7 +554,7 @@ pub(crate) mod legacy { let length = (read_length + min).div_ceil(2); - let mut read_numeric_string = vec![0; length as usize]; + let mut read_numeric_string = vec![0; usize::from(length)]; stream.read_exact(read_numeric_string.as_mut())?; Ok(()) @@ -536,7 +563,9 @@ pub(crate) mod legacy { pub(crate) fn write_numeric_string(mut stream: impl io::Write, num_str: &[u8], min: usize) -> io::Result { let length = if num_str.len() >= min { num_str.len() - min } else { min }; - let mut size = write_length(&mut stream, length as u16)?; + let length = u16::try_from(length) + .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid numeric string length"))?; + let mut size = write_length(&mut stream, length)?; let magic_transform = |elem| (elem - 0x30) % 10; diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs/mod.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs/mod.rs index cb42178f..66a9f8c0 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs/mod.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs/mod.rs @@ -141,10 +141,10 @@ impl<'de> Decode<'de> for BitmapCodecs { fn decode(src: &mut ReadCursor<'de>) -> DecodeResult { ensure_fixed_part_size!(in: src); - let codecs_count = src.read_u8(); + let codec_count = src.read_u8(); - let mut codecs = Vec::with_capacity(codecs_count as usize); - for _ in 0..codecs_count { + let mut codecs = Vec::with_capacity(usize::from(codec_count)); + for _ in 0..codec_count { codecs.push(Codec::decode(src)?); } @@ -552,7 +552,7 @@ impl<'de> Decode<'de> for RfxCapset { let num_icaps = src.read_u16(); let _icaps_len = src.read_u16(); - let mut icaps_data = Vec::with_capacity(num_icaps as usize); + let mut icaps_data = Vec::with_capacity(usize::from(num_icaps)); for _ in 0..num_icaps { icaps_data.push(RfxICap::decode(src)?); } diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/mod.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/mod.rs index 35737ac3..2662e95b 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/mod.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/mod.rs @@ -215,9 +215,9 @@ impl<'de> Decode<'de> for DemandActive { fn decode(src: &mut ReadCursor<'de>) -> DecodeResult { ensure_fixed_part_size!(in: src); - let source_descriptor_length = src.read_u16() as usize; + let source_descriptor_length = usize::from(src.read_u16()); // The combined size in bytes of the numberCapabilities, pad2Octets, and capabilitySets fields. - let _combined_capabilities_length = src.read_u16() as usize; + let _combined_capabilities_length = usize::from(src.read_u16()); ensure_size!(in: src, size: source_descriptor_length); let source_descriptor = utils::decode_string( @@ -227,7 +227,7 @@ impl<'de> Decode<'de> for DemandActive { )?; ensure_size!(in: src, size: 2 + 2); - let capability_sets_count = src.read_u16() as usize; + let capability_sets_count = usize::from(src.read_u16()); let _padding = src.read_u16(); let mut capability_sets = Vec::with_capacity(capability_sets_count); @@ -509,7 +509,7 @@ impl<'de> Decode<'de> for CapabilitySet { ) })?; - let length = src.read_u16() as usize; + let length = usize::from(src.read_u16()); if length < CAPABILITY_SET_TYPE_FIELD_SIZE + CAPABILITY_SET_LENGTH_FIELD_SIZE { return Err(invalid_field_err!("len", "invalid capability set length")); diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/order/mod.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/order/mod.rs index 30de1c57..2b3f8680 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/order/mod.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/order/mod.rs @@ -9,6 +9,7 @@ const ORD_LEVEL_1_ORDERS: u16 = 1; const SUPPORT_ARRAY_LEN: usize = 32; const DESKTOP_SAVE_Y_GRAN_VAL: u16 = 20; +#[repr(u8)] #[derive(Copy, Clone)] pub enum OrderSupportIndex { DstBlt = 0x00, @@ -34,6 +35,16 @@ pub enum OrderSupportIndex { Index = 0x1B, } +impl OrderSupportIndex { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] + fn as_u8(self) -> u8 { + self as u8 + } +} + bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct OrderFlags: u16 { @@ -83,11 +94,11 @@ impl Order { } pub fn set_support_flag(&mut self, flag: OrderSupportIndex, value: bool) { - self.order_support[flag as usize] = u8::from(value) + self.order_support[usize::from(flag.as_u8())] = u8::from(value) } pub fn get_support_flag(&mut self, flag: OrderSupportIndex) -> bool { - self.order_support[flag as usize] == 1 + self.order_support[usize::from(flag.as_u8())] == 1 } } diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/order/tests.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/order/tests.rs index 3f6c873e..995c3e4f 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/order/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/order/tests.rs @@ -30,27 +30,27 @@ lazy_static! { order_support: { let mut array = [0u8; 32]; - array[OrderSupportIndex::DstBlt as usize] = 1; - array[OrderSupportIndex::PatBlt as usize] = 1; - array[OrderSupportIndex::ScrBlt as usize] = 1; - array[OrderSupportIndex::MemBlt as usize] = 1; - array[OrderSupportIndex::Mem3Blt as usize] = 1; - array[OrderSupportIndex::DrawnInEGrid as usize] = 1; - array[OrderSupportIndex::LineTo as usize] = 1; - array[OrderSupportIndex::MultiDrawnInEGrid as usize] = 1; - array[OrderSupportIndex::SaveBitmap as usize] = 1; - array[OrderSupportIndex::MultiDstBlt as usize] = 1; - array[OrderSupportIndex::MultiPatBlt as usize] = 1; - array[OrderSupportIndex::MultiScrBlt as usize] = 1; - array[OrderSupportIndex::MultiOpaqueRect as usize] = 1; - array[OrderSupportIndex::Fast as usize] = 1; - array[OrderSupportIndex::PolygonSC as usize] = 1; - array[OrderSupportIndex::PolygonCB as usize] = 1; - array[OrderSupportIndex::Polyline as usize] = 1; - array[OrderSupportIndex::FastGlyph as usize] = 1; - array[OrderSupportIndex::EllipseSC as usize] = 1; - array[OrderSupportIndex::EllipseCB as usize] = 1; - array[OrderSupportIndex::Index as usize] = 1; + array[usize::from(OrderSupportIndex::DstBlt.as_u8())] = 1; + array[usize::from(OrderSupportIndex::PatBlt.as_u8())] = 1; + array[usize::from(OrderSupportIndex::ScrBlt.as_u8())] = 1; + array[usize::from(OrderSupportIndex::MemBlt.as_u8())] = 1; + array[usize::from(OrderSupportIndex::Mem3Blt.as_u8())] = 1; + array[usize::from(OrderSupportIndex::DrawnInEGrid.as_u8())] = 1; + array[usize::from(OrderSupportIndex::LineTo.as_u8())] = 1; + array[usize::from(OrderSupportIndex::MultiDrawnInEGrid.as_u8())] = 1; + array[usize::from(OrderSupportIndex::SaveBitmap.as_u8())] = 1; + array[usize::from(OrderSupportIndex::MultiDstBlt.as_u8())] = 1; + array[usize::from(OrderSupportIndex::MultiPatBlt.as_u8())] = 1; + array[usize::from(OrderSupportIndex::MultiScrBlt.as_u8())] = 1; + array[usize::from(OrderSupportIndex::MultiOpaqueRect.as_u8())] = 1; + array[usize::from(OrderSupportIndex::Fast.as_u8())] = 1; + array[usize::from(OrderSupportIndex::PolygonSC.as_u8())] = 1; + array[usize::from(OrderSupportIndex::PolygonCB.as_u8())] = 1; + array[usize::from(OrderSupportIndex::Polyline.as_u8())] = 1; + array[usize::from(OrderSupportIndex::FastGlyph.as_u8())] = 1; + array[usize::from(OrderSupportIndex::EllipseSC.as_u8())] = 1; + array[usize::from(OrderSupportIndex::EllipseCB.as_u8())] = 1; + array[usize::from(OrderSupportIndex::Index.as_u8())] = 1; array }, diff --git a/crates/ironrdp-pdu/src/rdp/client_info.rs b/crates/ironrdp-pdu/src/rdp/client_info.rs index 3f8ff727..4180fbec 100644 --- a/crates/ironrdp-pdu/src/rdp/client_info.rs +++ b/crates/ironrdp-pdu/src/rdp/client_info.rs @@ -145,9 +145,8 @@ impl<'de> Decode<'de> for ClientInfo { let flags = ClientInfoFlags::from_bits(flags_with_compression_type & !COMPRESSION_TYPE_MASK) .ok_or_else(|| invalid_field_err!("flags", "invalid ClientInfoFlags"))?; - let compression_type = - CompressionType::from_u8(((flags_with_compression_type & COMPRESSION_TYPE_MASK) >> 9) as u8) - .ok_or_else(|| invalid_field_err!("flags", "invalid CompressionType"))?; + let compression_type = CompressionType::from_u32((flags_with_compression_type & COMPRESSION_TYPE_MASK) >> 9) + .ok_or_else(|| invalid_field_err!("flags", "invalid CompressionType"))?; let character_set = if flags.contains(ClientInfoFlags::UNICODE) { CharacterSet::Unicode @@ -157,11 +156,11 @@ impl<'de> Decode<'de> for ClientInfo { // Sizes exclude the length of the mandatory null terminator let nt = usize::from(character_set.as_u16()); - let domain_size = src.read_u16() as usize + nt; - let user_name_size = src.read_u16() as usize + nt; - let password_size = src.read_u16() as usize + nt; - let alternate_shell_size = src.read_u16() as usize + nt; - let work_dir_size = src.read_u16() as usize + nt; + let domain_size = usize::from(src.read_u16()) + nt; + let user_name_size = usize::from(src.read_u16()) + nt; + let password_size = usize::from(src.read_u16()) + nt; + let alternate_shell_size = usize::from(src.read_u16()) + nt; + let work_dir_size = usize::from(src.read_u16()) + nt; ensure_size!(in: src, size: domain_size + user_name_size + password_size + alternate_shell_size + work_dir_size); let domain = utils::decode_string(src.read_slice(domain_size), character_set, true)?; @@ -226,12 +225,12 @@ impl ExtendedClientInfo { let address_family = AddressFamily::from_u16(src.read_u16()); // This size includes the length of the mandatory null terminator. - let address_size = src.read_u16() as usize; + let address_size = usize::from(src.read_u16()); ensure_size!(in: src, size: address_size + CLIENT_DIR_LENGTH_SIZE); let address = utils::decode_string(src.read_slice(address_size), character_set, false)?; // This size includes the length of the mandatory null terminator. - let dir_size = src.read_u16() as usize; + let dir_size = usize::from(src.read_u16()); ensure_size!(in: src, size: dir_size); let dir = utils::decode_string(src.read_slice(dir_size), character_set, false)?; @@ -324,7 +323,7 @@ impl Encode for ExtendedClientOptionalInfo { dst.write_u32(performance_flags.bits()); } if let Some(reconnect_cookie) = self.reconnect_cookie { - dst.write_u16(RECONNECT_COOKIE_LEN as u16); + dst.write_u16(u16::try_from(RECONNECT_COOKIE_LEN).expect("RECONNECT_COOKIE_LEN fit into u16")); dst.write_array(reconnect_cookie); } @@ -381,7 +380,9 @@ impl<'de> Decode<'de> for ExtendedClientOptionalInfo { return Ok(optional_data); } let reconnect_cookie_size = src.read_u16(); - if reconnect_cookie_size != RECONNECT_COOKIE_LEN as u16 && reconnect_cookie_size != 0 { + if reconnect_cookie_size != u16::try_from(RECONNECT_COOKIE_LEN).expect("RECONNECT_COOKIE_LEN fit into u16") + && reconnect_cookie_size != 0 + { return Err(invalid_field_err!("cbAutoReconnectCookie", "invalid cookie size")); } if reconnect_cookie_size != 0 { diff --git a/crates/ironrdp-pdu/src/rdp/finalization_messages.rs b/crates/ironrdp-pdu/src/rdp/finalization_messages.rs index 480066ba..bea2710d 100644 --- a/crates/ironrdp-pdu/src/rdp/finalization_messages.rs +++ b/crates/ironrdp-pdu/src/rdp/finalization_messages.rs @@ -220,7 +220,10 @@ impl<'de> Decode<'de> for MonitorLayoutPdu { return Err(invalid_field_err!("nMonitors", "invalid monitor count")); } - let mut monitors = Vec::with_capacity(monitor_count as usize); + let mut monitors = Vec::with_capacity( + usize::try_from(monitor_count) + .expect("monitor_count is guaranteed to fit into usize due to the prior check"), + ); for _ in 0..monitor_count { monitors.push(gcc::Monitor::decode(src)?); } diff --git a/crates/ironrdp-pdu/src/rdp/headers.rs b/crates/ironrdp-pdu/src/rdp/headers.rs index f469f52c..e97c8add 100644 --- a/crates/ironrdp-pdu/src/rdp/headers.rs +++ b/crates/ironrdp-pdu/src/rdp/headers.rs @@ -115,7 +115,7 @@ impl<'de> Decode<'de> for ShareControlHeader { fn decode(src: &mut ReadCursor<'de>) -> DecodeResult { ensure_fixed_part_size!(in: src); - let total_length = src.read_u16() as usize; + let total_length = usize::from(src.read_u16()); let pdu_type_with_version = src.read_u16(); let pdu_source = src.read_u16(); let share_id = src.read_u32(); diff --git a/crates/ironrdp-pdu/src/rdp/server_license/client_license_info.rs b/crates/ironrdp-pdu/src/rdp/server_license/client_license_info.rs index bec11af5..4c72032d 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/client_license_info.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/client_license_info.rs @@ -80,7 +80,7 @@ impl ClientLicenseInfo { let mut rc4 = Rc4::new(&license_key); let encrypted_hwid = rc4.process(&hardware_id); - let mac_data = compute_mac_data(mac_salt_key, &hardware_id); + let mac_data = compute_mac_data(mac_salt_key, &hardware_id)?; let size = RANDOM_NUMBER_SIZE + PREAMBLE_SIZE @@ -97,7 +97,8 @@ impl ClientLicenseInfo { preamble_message_type: PreambleType::LicenseInfo, preamble_flags: PreambleFlags::empty(), preamble_version: PreambleVersion::V3, - preamble_message_size: (size) as u16, + preamble_message_size: u16::try_from(size) + .map_err(|_| ServerLicenseError::InvalidField("preamble message size"))?, }; Ok(( diff --git a/crates/ironrdp-pdu/src/rdp/server_license/client_new_license_request/mod.rs b/crates/ironrdp-pdu/src/rdp/server_license/client_new_license_request/mod.rs index 1ae1d666..adaacc86 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/client_new_license_request/mod.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/client_new_license_request/mod.rs @@ -98,14 +98,17 @@ impl ClientNewLicenseRequest { preamble_message_type: PreambleType::NewLicenseRequest, preamble_flags: PreambleFlags::empty(), preamble_version: PreambleVersion::V3, - preamble_message_size: (RANDOM_NUMBER_SIZE - + PREAMBLE_SIZE - + LICENSE_REQUEST_STATIC_FIELDS_SIZE - + encrypted_premaster_secret.len() - + client_machine_name.len() - + UTF8_NULL_TERMINATOR_SIZE - + client_username.len() - + UTF8_NULL_TERMINATOR_SIZE) as u16, + preamble_message_size: u16::try_from( + RANDOM_NUMBER_SIZE + + PREAMBLE_SIZE + + LICENSE_REQUEST_STATIC_FIELDS_SIZE + + encrypted_premaster_secret.len() + + client_machine_name.len() + + UTF8_NULL_TERMINATOR_SIZE + + client_username.len() + + UTF8_NULL_TERMINATOR_SIZE, + ) + .map_err(|_| ServerLicenseError::InvalidField("preamble message size"))?, }; Ok(( diff --git a/crates/ironrdp-pdu/src/rdp/server_license/client_new_license_request/tests.rs b/crates/ironrdp-pdu/src/rdp/server_license/client_new_license_request/tests.rs index 302285e5..8cd33340 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/client_new_license_request/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/client_new_license_request/tests.rs @@ -68,14 +68,14 @@ lazy_static! { preamble_message_type: PreambleType::NewLicenseRequest, preamble_flags: PreambleFlags::empty(), preamble_version: PreambleVersion::V3, - preamble_message_size: (PREAMBLE_SIZE + preamble_message_size: u16::try_from(PREAMBLE_SIZE + RANDOM_NUMBER_SIZE + LICENSE_REQUEST_STATIC_FIELDS_SIZE + ENCRYPTED_PREMASTER_SECRET.len() + CLIENT_MACHINE_NAME.len() + UTF8_NULL_TERMINATOR_SIZE + CLIENT_USERNAME.len() - + UTF8_NULL_TERMINATOR_SIZE) as u16, + + UTF8_NULL_TERMINATOR_SIZE).expect("can't panic"), }, client_random: Vec::from(CLIENT_RANDOM_BUFFER.as_ref()), encrypted_premaster_secret: Vec::from(ENCRYPTED_PREMASTER_SECRET.as_ref()), @@ -86,11 +86,11 @@ lazy_static! { pub static ref REQUEST_BUFFER: Vec = { let username_len = CLIENT_USERNAME.len() + UTF8_NULL_TERMINATOR_SIZE; let mut username_len_buf = Vec::new(); - username_len_buf.write_u16::(username_len as u16).unwrap(); + username_len_buf.write_u16::(u16::try_from(username_len).expect("can't panic")).unwrap(); let machine_name_len = CLIENT_MACHINE_NAME.len() + UTF8_NULL_TERMINATOR_SIZE; let mut machine_name_len_buf = Vec::new(); - machine_name_len_buf.write_u16::(machine_name_len as u16).unwrap(); + machine_name_len_buf.write_u16::(u16::try_from(machine_name_len).unwrap()).unwrap(); let buf = [ &[0x01u8, 0x00, 0x00, 0x00, // preferred_key_exchange_algorithm @@ -109,7 +109,7 @@ lazy_static! { &[0x00]] // null .concat(); - let preamble_size_field = (buf.len() + PREAMBLE_SIZE) as u16; + let preamble_size_field = u16::try_from(buf.len() + PREAMBLE_SIZE).expect("can't panic"); [ LICENSE_HEADER_BUFFER_NO_SIZE.as_ref(), @@ -270,7 +270,7 @@ lazy_static! { }), scope_list: vec![Scope(String::from("microsoft.com"))], }; - req.license_header.preamble_message_size = req.size() as u16; + req.license_header.preamble_message_size = u16::try_from(req.size()).expect("can't panic"); req.into() }; } diff --git a/crates/ironrdp-pdu/src/rdp/server_license/client_platform_challenge_response/mod.rs b/crates/ironrdp-pdu/src/rdp/server_license/client_platform_challenge_response/mod.rs index 0bf408bf..d00e068a 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/client_platform_challenge_response/mod.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/client_platform_challenge_response/mod.rs @@ -46,7 +46,7 @@ impl ClientPlatformChallengeResponse { let decrypted_challenge = rc4.process(platform_challenge.encrypted_platform_challenge.as_slice()); let decrypted_challenge_mac = - super::compute_mac_data(encryption_data.mac_salt_key.as_slice(), decrypted_challenge.as_slice()); + super::compute_mac_data(encryption_data.mac_salt_key.as_slice(), decrypted_challenge.as_slice())?; if decrypted_challenge_mac != platform_challenge.mac_data { return Err(ServerLicenseError::InvalidMacData); @@ -56,7 +56,10 @@ impl ClientPlatformChallengeResponse { challenge_response_data.write_u16::(RESPONSE_DATA_VERSION)?; challenge_response_data.write_u16::(ClientType::Other.as_u16())?; challenge_response_data.write_u16::(LicenseDetailLevel::Detail.as_u16())?; - challenge_response_data.write_u16::(decrypted_challenge.len() as u16)?; + challenge_response_data.write_u16::( + u16::try_from(decrypted_challenge.len()) + .map_err(|_| ServerLicenseError::InvalidField("decrypted challenge len"))?, + )?; challenge_response_data.write_all(&decrypted_challenge)?; let mut hardware_id = Vec::with_capacity(CLIENT_HARDWARE_IDENTIFICATION_SIZE); @@ -75,7 +78,7 @@ impl ClientPlatformChallengeResponse { let mac_data = super::compute_mac_data( encryption_data.mac_salt_key.as_slice(), challenge_response_data.as_slice(), - ); + )?; let license_header = LicenseHeader { security_header: BasicSecurityHeader { @@ -84,10 +87,12 @@ impl ClientPlatformChallengeResponse { preamble_message_type: PreambleType::PlatformChallengeResponse, preamble_flags: PreambleFlags::empty(), preamble_version: PreambleVersion::V3, - preamble_message_size: (PREAMBLE_SIZE + preamble_message_size: u16::try_from( + PREAMBLE_SIZE + (BLOB_TYPE_SIZE + BLOB_LENGTH_SIZE) * 2 // 2 blobs in this structure - + MAC_SIZE + encrypted_challenge_response_data.len() + encrypted_hwid.len()) - as u16, + + MAC_SIZE + encrypted_challenge_response_data.len() + encrypted_hwid.len(), + ) + .map_err(|_| ServerLicenseError::InvalidField("preamble message size"))?, }; Ok(Self { diff --git a/crates/ironrdp-pdu/src/rdp/server_license/client_platform_challenge_response/test.rs b/crates/ironrdp-pdu/src/rdp/server_license/client_platform_challenge_response/test.rs index ce298e81..3ade8770 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/client_platform_challenge_response/test.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/client_platform_challenge_response/test.rs @@ -63,8 +63,10 @@ lazy_static! { preamble_message_type: PreambleType::PlatformChallengeResponse, preamble_flags: PreambleFlags::empty(), preamble_version: PreambleVersion::V3, - preamble_message_size: (CLIENT_PLATFORM_CHALLENGE_RESPONSE_BUFFER.len() - BASIC_SECURITY_HEADER_SIZE) - as u16, + preamble_message_size: u16::try_from( + CLIENT_PLATFORM_CHALLENGE_RESPONSE_BUFFER.len() - BASIC_SECURITY_HEADER_SIZE + ) + .expect("can't panic"), }, encrypted_challenge_response_data: Vec::from(&CLIENT_PLATFORM_CHALLENGE_RESPONSE_BUFFER[12..30]), encrypted_hwid: Vec::from(&CLIENT_PLATFORM_CHALLENGE_RESPONSE_BUFFER[34..54]), @@ -165,7 +167,8 @@ fn challenge_response_creates_from_server_challenge_and_encryption_data_correctl preamble_message_type: PreambleType::PlatformChallenge, preamble_flags: PreambleFlags::empty(), preamble_version: PreambleVersion::V3, - preamble_message_size: (encrypted_platform_challenge.len() + mac_data.len() + PREAMBLE_SIZE) as u16, + preamble_message_size: u16::try_from(encrypted_platform_challenge.len() + mac_data.len() + PREAMBLE_SIZE) + .expect("can't panic"), }, encrypted_platform_challenge, mac_data, @@ -200,7 +203,8 @@ fn challenge_response_creates_from_server_challenge_and_encryption_data_correctl let mac_data = crate::rdp::server_license::compute_mac_data( encryption_data.mac_salt_key.as_slice(), [response_data.as_ref(), hardware_id.as_slice()].concat().as_slice(), - ); + ) + .expect("can't panic"); let correct_challenge_response = ClientPlatformChallengeResponse { license_header: LicenseHeader { @@ -210,10 +214,12 @@ fn challenge_response_creates_from_server_challenge_and_encryption_data_correctl preamble_message_type: PreambleType::PlatformChallengeResponse, preamble_flags: PreambleFlags::empty(), preamble_version: PreambleVersion::V3, - preamble_message_size: (PREAMBLE_SIZE + preamble_message_size: u16::try_from( + PREAMBLE_SIZE + (BLOB_TYPE_SIZE + BLOB_LENGTH_SIZE) * 2 // 2 blobs in this structure - + MAC_SIZE + encrypted_challenge_response_data.len() + encrypted_hwid.len()) - as u16, + + MAC_SIZE + encrypted_challenge_response_data.len() + encrypted_hwid.len(), + ) + .expect("can't panic"), }, encrypted_challenge_response_data, encrypted_hwid, diff --git a/crates/ironrdp-pdu/src/rdp/server_license/licensing_error_message/test.rs b/crates/ironrdp-pdu/src/rdp/server_license/licensing_error_message/test.rs index 588e6d18..5ef7cb03 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/licensing_error_message/test.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/licensing_error_message/test.rs @@ -26,7 +26,7 @@ lazy_static! { state_transition: LicensingStateTransition::NoTransition, error_info: Vec::new(), }; - pdu.license_header.preamble_message_size = pdu.size() as u16; + pdu.license_header.preamble_message_size = u16::try_from(pdu.size()).expect("can't panic"); pdu.into() }; } diff --git a/crates/ironrdp-pdu/src/rdp/server_license/mod.rs b/crates/ironrdp-pdu/src/rdp/server_license/mod.rs index ee1b2bf0..170ce33c 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/mod.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/mod.rs @@ -207,6 +207,8 @@ pub enum ServerLicenseError { Utf8Error(#[from] std::string::FromUtf8Error), #[error("DER error: {0}")] DerError(#[from] pkcs1::der::Error), + #[error("invalid `{0}`: out of range integral type conversion")] + InvalidField(&'static str), #[error("invalid preamble field: {0}")] InvalidPreamble(String), #[error("invalid preamble message type field")] @@ -338,8 +340,10 @@ impl<'de> Decode<'de> for BlobHeader { } } -fn compute_mac_data(mac_salt_key: &[u8], data: &[u8]) -> Vec { - let data_len_buffer = (data.len() as u32).to_le_bytes(); +fn compute_mac_data(mac_salt_key: &[u8], data: &[u8]) -> Result, ServerLicenseError> { + let data_len_buffer = u32::try_from(data.len()) + .map_err(|_| ServerLicenseError::InvalidField("MAC data length"))? + .to_le_bytes(); let pad_one: [u8; 40] = [0x36; 40]; @@ -360,7 +364,7 @@ fn compute_mac_data(mac_salt_key: &[u8], data: &[u8]) -> Vec { .as_slice(), ); - md5.finalize().to_vec() + Ok(md5.finalize().to_vec()) } #[derive(Debug, PartialEq)] diff --git a/crates/ironrdp-pdu/src/rdp/server_license/server_license_request/mod.rs b/crates/ironrdp-pdu/src/rdp/server_license/server_license_request/mod.rs index 3ed5cfe4..e4c5ee38 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/server_license_request/mod.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/server_license_request/mod.rs @@ -108,7 +108,10 @@ impl ServerLicenseRequest { return Err(invalid_field_err!("scopeCount", "invalid scope count")); } - let mut scope_list = Vec::with_capacity(scope_count as usize); + let mut scope_list = Vec::with_capacity( + #[expect(clippy::missing_panics_doc, reason = "unreachable panic (checked integer underflow)")] + usize::try_from(scope_count).expect("scope_count is guaranteed to fit into usize due to the prior check"), + ); for _ in 0..scope_count { scope_list.push(Scope::decode(src)?); diff --git a/crates/ironrdp-pdu/src/rdp/server_license/server_license_request/tests.rs b/crates/ironrdp-pdu/src/rdp/server_license/server_license_request/tests.rs index a29e4226..d6dc7314 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/server_license_request/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/server_license_request/tests.rs @@ -252,7 +252,7 @@ lazy_static! { }), scope_list: vec![Scope(String::from("microsoft.com"))], }; - req.license_header.preamble_message_size = req.size() as u16; + req.license_header.preamble_message_size = u16::try_from(req.size()).expect("can't panic"); req.into() }; pub static ref X509_CERTIFICATE: ServerCertificate = ServerCertificate { @@ -323,7 +323,7 @@ fn from_buffer_correctly_parses_server_license_request_no_certificate() { server_certificate: None, scope_list: vec![Scope(String::from("microsoft.com"))], }; - request.license_header.preamble_message_size = request.size() as u16; + request.license_header.preamble_message_size = u16::try_from(request.size()).expect("can't panic"); let request: LicensePdu = request.into(); assert_eq!(request, decode(&request_buffer).unwrap()); @@ -370,7 +370,7 @@ fn to_buffer_correctly_serializes_server_license_request() { }), scope_list: vec![Scope(String::from("microsoft.com"))], }; - request.license_header.preamble_message_size = request.size() as u16; + request.license_header.preamble_message_size = u16::try_from(request.size()).unwrap(); let request: LicensePdu = request.into(); let serialized_request = encode_vec(&request).unwrap(); diff --git a/crates/ironrdp-pdu/src/rdp/server_license/server_platform_challenge/test.rs b/crates/ironrdp-pdu/src/rdp/server_license/server_platform_challenge/test.rs index ac61d260..7de7fa56 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/server_platform_challenge/test.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/server_platform_challenge/test.rs @@ -35,7 +35,8 @@ lazy_static! { preamble_message_type: PreambleType::PlatformChallenge, preamble_flags: PreambleFlags::empty(), preamble_version: PreambleVersion::V3, - preamble_message_size: (PLATFORM_CHALLENGE_BUFFER.len() - BASIC_SECURITY_HEADER_SIZE) as u16, + preamble_message_size: u16::try_from(PLATFORM_CHALLENGE_BUFFER.len() - BASIC_SECURITY_HEADER_SIZE) + .expect("can't panic"), }, encrypted_platform_challenge: Vec::from(CHALLENGE_BUFFER.as_ref()), mac_data: Vec::from(MAC_DATA_BUFFER.as_ref()), diff --git a/crates/ironrdp-pdu/src/rdp/server_license/server_upgrade_license/mod.rs b/crates/ironrdp-pdu/src/rdp/server_license/server_upgrade_license/mod.rs index 492f188e..72671172 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/server_upgrade_license/mod.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/server_upgrade_license/mod.rs @@ -69,7 +69,7 @@ impl ServerUpgradeLicense { pub fn verify_server_license(&self, encryption_data: &LicenseEncryptionData) -> Result<(), ServerLicenseError> { let decrypted_license_info = self.decrypted_license_info(encryption_data); let mac_data = - super::compute_mac_data(encryption_data.mac_salt_key.as_slice(), decrypted_license_info.as_ref()); + super::compute_mac_data(encryption_data.mac_salt_key.as_slice(), decrypted_license_info.as_ref())?; if mac_data != self.mac_data { return Err(ServerLicenseError::InvalidMacData); diff --git a/crates/ironrdp-pdu/src/rdp/server_license/server_upgrade_license/tests.rs b/crates/ironrdp-pdu/src/rdp/server_license/server_upgrade_license/tests.rs index 6aa58641..3f4a5f24 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/server_upgrade_license/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/server_upgrade_license/tests.rs @@ -260,7 +260,8 @@ lazy_static! { preamble_message_type: PreambleType::NewLicense, preamble_flags: PreambleFlags::empty(), preamble_version: PreambleVersion::V3, - preamble_message_size: (SERVER_UPGRADE_LICENSE_BUFFER.len() - BASIC_SECURITY_HEADER_SIZE) as u16, + preamble_message_size: u16::try_from(SERVER_UPGRADE_LICENSE_BUFFER.len() - BASIC_SECURITY_HEADER_SIZE) + .expect("buffer size is too large"), }, encrypted_license_info: Vec::from( &SERVER_UPGRADE_LICENSE_BUFFER[12..SERVER_UPGRADE_LICENSE_BUFFER.len() - MAC_SIZE] @@ -618,11 +619,10 @@ fn upgrade_license_verifies_correctly() { preamble_message_type: PreambleType::NewLicense, preamble_flags: PreambleFlags::empty(), preamble_version: PreambleVersion::V3, - preamble_message_size: (PREAMBLE_SIZE - + BLOB_LENGTH_SIZE - + BLOB_TYPE_SIZE - + encrypted_license_info.len() - + MAC_SIZE) as u16, + preamble_message_size: u16::try_from( + PREAMBLE_SIZE + BLOB_LENGTH_SIZE + BLOB_TYPE_SIZE + encrypted_license_info.len() + MAC_SIZE, + ) + .expect("can't panic"), }, encrypted_license_info, mac_data, diff --git a/crates/ironrdp-pdu/src/rdp/server_license/tests.rs b/crates/ironrdp-pdu/src/rdp/server_license/tests.rs index 27e12c61..c4880a7a 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/tests.rs @@ -106,7 +106,7 @@ fn mac_data_computes_correctly() { let decrypted_server_challenge: [u8; 10] = [0x54, 0x0, 0x45, 0x0, 0x53, 0x0, 0x54, 0x0, 0x0, 0x0]; assert_eq!( - compute_mac_data(mac_salt_key.as_ref(), decrypted_server_challenge.as_ref()), + compute_mac_data(mac_salt_key.as_ref(), decrypted_server_challenge.as_ref()).unwrap(), server_mac_data.as_ref() ); } diff --git a/crates/ironrdp-pdu/src/rdp/session_info/logon_extended.rs b/crates/ironrdp-pdu/src/rdp/session_info/logon_extended.rs index 47c195dc..88a42e5c 100644 --- a/crates/ironrdp-pdu/src/rdp/session_info/logon_extended.rs +++ b/crates/ironrdp-pdu/src/rdp/session_info/logon_extended.rs @@ -112,8 +112,8 @@ impl Encode for ServerAutoReconnect { fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { ensure_fixed_part_size!(in: dst); - dst.write_u32(AUTO_RECONNECT_PACKET_SIZE as u32); - dst.write_u32(AUTO_RECONNECT_PACKET_SIZE as u32); + dst.write_u32(u32::try_from(AUTO_RECONNECT_PACKET_SIZE).expect("AUTO_RECONNECT_PACKET_SIZE fits into u32")); + dst.write_u32(u32::try_from(AUTO_RECONNECT_PACKET_SIZE).expect("AUTO_RECONNECT_PACKET_SIZE fits into u32")); dst.write_u32(AUTO_RECONNECT_VERSION_1); dst.write_u32(self.logon_id); dst.write_slice(self.random_bits.as_ref()); @@ -136,7 +136,8 @@ impl<'de> Decode<'de> for ServerAutoReconnect { let _data_length = src.read_u32(); let packet_length = src.read_u32(); - if packet_length != AUTO_RECONNECT_PACKET_SIZE as u32 { + if packet_length != u32::try_from(AUTO_RECONNECT_PACKET_SIZE).expect("AUTO_RECONNECT_PACKET_SIZE fits into u32") + { return Err(invalid_field_err!("packetLen", "invalid auto-reconnect packet size")); } @@ -171,7 +172,7 @@ impl Encode for LogonErrorsInfo { fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { ensure_fixed_part_size!(in: dst); - dst.write_u32(LOGON_ERRORS_INFO_SIZE as u32); + dst.write_u32(u32::try_from(LOGON_ERRORS_INFO_SIZE).expect("LOGON_ERRORS_INFO_SIZE fits into u32")); dst.write_u32(self.error_type.as_u32()); dst.write_u32(self.error_data.to_u32()); diff --git a/crates/ironrdp-pdu/src/rdp/session_info/logon_info.rs b/crates/ironrdp-pdu/src/rdp/session_info/logon_info.rs index 8555a5fe..e30c54b2 100644 --- a/crates/ironrdp-pdu/src/rdp/session_info/logon_info.rs +++ b/crates/ironrdp-pdu/src/rdp/session_info/logon_info.rs @@ -111,7 +111,7 @@ impl Encode for LogonInfoVersion2 { ensure_size!(in: dst, size: self.size()); dst.write_u16(SAVE_SESSION_PDU_VERSION_ONE); - dst.write_u32(LOGON_INFO_V2_SIZE as u32); + dst.write_u32(u32::try_from(LOGON_INFO_V2_SIZE).expect("LOGON_INFO_V2_SIZE fits into u32")); dst.write_u32(self.logon_info.session_id); dst.write_u32(cast_length!( "domainNameSize", diff --git a/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/avc_messages.rs b/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/avc_messages.rs index 501c44c3..73b17078 100644 --- a/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/avc_messages.rs +++ b/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/avc_messages.rs @@ -112,9 +112,9 @@ impl<'de> Decode<'de> for Avc420BitmapStream<'de> { fn decode(src: &mut ReadCursor<'de>) -> DecodeResult { ensure_fixed_part_size!(in: src); - let num_regions = src.read_u32(); - let mut rectangles = Vec::with_capacity(num_regions as usize); - let mut quant_qual_vals = Vec::with_capacity(num_regions as usize); + let num_regions = cast_length!("number of regions", src.read_u32())?; + let mut rectangles = Vec::with_capacity(num_regions); + let mut quant_qual_vals = Vec::with_capacity(num_regions); for _ in 0..num_regions { rectangles.push(InclusiveRectangle::decode(src)?); } @@ -158,7 +158,7 @@ impl Encode for Avc444BitmapStream<'_> { let mut stream_info = 0u32; stream_info.set_bits(0..30, cast_length!("stream1size", self.stream1.size())?); - stream_info.set_bits(30..32, self.encoding.bits() as u32); + stream_info.set_bits(30..32, u32::from(self.encoding.bits())); dst.write_u32(stream_info); self.stream1.encode(dst)?; if let Some(stream) = self.stream2.as_ref() { @@ -188,7 +188,8 @@ impl<'de> Decode<'de> for Avc444BitmapStream<'de> { let stream_info = src.read_u32(); let stream_len = stream_info.get_bits(0..30); - let encoding = Encoding::from_bits_truncate(stream_info.get_bits(30..32) as u8); + let encoding = + Encoding::from_bits_truncate(u8::try_from(stream_info.get_bits(30..32)).expect("value fits into u8")); if stream_len == 0 { if encoding == Encoding::LUMA_AND_CHROMA { @@ -202,7 +203,7 @@ impl<'de> Decode<'de> for Avc444BitmapStream<'de> { stream2: None, }) } else { - let (mut stream1, mut stream2) = src.split_at(stream_len as usize); + let (mut stream1, mut stream2) = src.split_at(cast_length!("first stream length", stream_len)?); let stream1 = Avc420BitmapStream::decode(&mut stream1)?; let stream2 = if encoding == Encoding::LUMA_AND_CHROMA { Some(Avc420BitmapStream::decode(&mut stream2)?) diff --git a/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/server.rs b/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/server.rs index f2046d15..3317895c 100644 --- a/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/server.rs +++ b/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/server.rs @@ -231,7 +231,7 @@ impl Encode for SolidFillPdu { dst.write_u16(self.surface_id); self.fill_pixel.encode(dst)?; - dst.write_u16(self.rectangles.len() as u16); + dst.write_u16(cast_length!("number of rectangles", self.rectangles.len())?); for rectangle in self.rectangles.iter() { rectangle.encode(dst)?; @@ -1026,10 +1026,10 @@ impl<'a> Decode<'a> for Timestamp { let timestamp = src.read_u32(); - let milliseconds = timestamp.get_bits(..10) as u16; - let seconds = timestamp.get_bits(10..16) as u8; - let minutes = timestamp.get_bits(16..22) as u8; - let hours = timestamp.get_bits(22..) as u16; + let milliseconds = u16::try_from(timestamp.get_bits(..10)).expect("value fits into u16"); + let seconds = u8::try_from(timestamp.get_bits(10..16)).expect("value fits into u8"); + let minutes = u8::try_from(timestamp.get_bits(16..22)).expect("value fits into u8"); + let hours = u16::try_from(timestamp.get_bits(22..)).expect("value fits into u16"); Ok(Self { milliseconds, diff --git a/crates/ironrdp-server/src/server.rs b/crates/ironrdp-server/src/server.rs index 101ea406..9023a4cc 100644 --- a/crates/ironrdp-server/src/server.rs +++ b/crates/ironrdp-server/src/server.rs @@ -823,7 +823,7 @@ impl RdpServer { } async fn handle_fastpath(&mut self, input: FastPathInput) { - for event in input.0 { + for event in input.input_events().iter().copied() { let mut handler = self.handler.lock().await; match event { FastPathInputEvent::KeyboardEvent(flags, key) => { diff --git a/crates/ironrdp-session/src/active_stage.rs b/crates/ironrdp-session/src/active_stage.rs index 5ae3f80e..75ef5499 100644 --- a/crates/ironrdp-session/src/active_stage.rs +++ b/crates/ironrdp-session/src/active_stage.rs @@ -68,7 +68,7 @@ impl ActiveStage { // Encoding fastpath response frame // PERF: unnecessary copy - let fastpath_input = FastPathInput(events.to_vec()); + let fastpath_input = FastPathInput::new(events.to_vec()).map_err(SessionError::decode)?; let frame = ironrdp_core::encode_vec(&fastpath_input).map_err(SessionError::encode)?; output.push(ActiveStageOutput::ResponseFrame(frame)); diff --git a/crates/ironrdp-testsuite-core/tests/pdu/input.rs b/crates/ironrdp-testsuite-core/tests/pdu/input.rs index 6bc1b54d..63e26682 100644 --- a/crates/ironrdp-testsuite-core/tests/pdu/input.rs +++ b/crates/ironrdp-testsuite-core/tests/pdu/input.rs @@ -10,7 +10,7 @@ const FASTPATH_INPUT_MESSAGE: [u8; 44] = [ ]; lazy_static::lazy_static! { - pub static ref FASTPATH_INPUT: FastPathInput = FastPathInput(vec![ + pub static ref FASTPATH_INPUT: FastPathInput = FastPathInput::new(vec![ FastPathInputEvent::MouseEvent(MousePdu { flags: PointerFlags::DOWN | PointerFlags::LEFT_BUTTON, number_of_wheel_rotation_units: 0, @@ -47,7 +47,7 @@ lazy_static::lazy_static! { x_position: 25, y_position: 1064 }) - ]); + ]).expect("can't panic"); } #[test] diff --git a/crates/ironrdp-web/src/session.rs b/crates/ironrdp-web/src/session.rs index 57338973..a3e189a7 100644 --- a/crates/ironrdp-web/src/session.rs +++ b/crates/ironrdp-web/src/session.rs @@ -781,7 +781,7 @@ impl iron_remote_desktop::Session for Session { use ironrdp::pdu::input::fast_path::FastPathInput; let event = ironrdp::input::synchronize_event(scroll_lock, num_lock, caps_lock, kana_lock); - let fastpath_input = FastPathInput(vec![event]); + let fastpath_input = FastPathInput::single(event); let frame = ironrdp::core::encode_vec(&fastpath_input).context("FastPathInput encoding")?; From da38fa20a31564bd508771040934468248553009 Mon Sep 17 00:00:00 2001 From: Alex Yusiuk <55661041+RRRadicalEdward@users.noreply.github.com> Date: Tue, 7 Oct 2025 13:46:35 +0300 Subject: [PATCH 14/51] chore(web): bump iron-remote-desktop-rdp to 0.6.1 (#1012) --- web-client/iron-remote-desktop-rdp/public/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web-client/iron-remote-desktop-rdp/public/package.json b/web-client/iron-remote-desktop-rdp/public/package.json index fc788cfc..8fb1a386 100644 --- a/web-client/iron-remote-desktop-rdp/public/package.json +++ b/web-client/iron-remote-desktop-rdp/public/package.json @@ -6,7 +6,7 @@ "Benoit Cortier" ], "description": "RDP backend for iron-remote-desktop", - "version": "0.6.0", + "version": "0.6.1", "main": "iron-remote-desktop-rdp.js", "types": "index.d.ts", "files": [ From cca53fd79b6b0add7400f020ecc4bfe964382355 Mon Sep 17 00:00:00 2001 From: Alex Yusiuk <55661041+RRRadicalEdward@users.noreply.github.com> Date: Tue, 7 Oct 2025 13:47:11 +0300 Subject: [PATCH 15/51] chore(web): bump iron-remote-desktop to 0.10.1 (#1011) --- web-client/iron-remote-desktop/public/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web-client/iron-remote-desktop/public/package.json b/web-client/iron-remote-desktop/public/package.json index e35db50c..d7e941ec 100644 --- a/web-client/iron-remote-desktop/public/package.json +++ b/web-client/iron-remote-desktop/public/package.json @@ -10,7 +10,7 @@ "Alexandr Yusuk" ], "description": "Backend-agnostic Web Component for remote desktop protocols", - "version": "0.10.0", + "version": "0.10.1", "main": "iron-remote-desktop.js", "types": "index.d.ts", "files": [ From a24a1fa9e8f1898b2fcdd41d87660ab9e38f89ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 7 Oct 2025 09:43:36 -0400 Subject: [PATCH 16/51] build(deps): bump bytemuck from 1.23.2 to 1.24.0 (#1008) --- Cargo.lock | 8 ++++---- crates/ironrdp-graphics/Cargo.toml | 2 +- crates/ironrdp-rdpsnd-native/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78d07bb3..7a3ffe06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -523,18 +523,18 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytemuck" -version = "1.23.2" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.10.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", diff --git a/crates/ironrdp-graphics/Cargo.toml b/crates/ironrdp-graphics/Cargo.toml index 43e4b5e5..4111a181 100644 --- a/crates/ironrdp-graphics/Cargo.toml +++ b/crates/ironrdp-graphics/Cargo.toml @@ -29,7 +29,7 @@ yuv = { version = "0.8", features = ["rdp"] } [dev-dependencies] bmp = "0.5" -bytemuck = "1.23" +bytemuck = "1.24" expect-test.workspace = true [lints] diff --git a/crates/ironrdp-rdpsnd-native/Cargo.toml b/crates/ironrdp-rdpsnd-native/Cargo.toml index 26683271..d99070a6 100644 --- a/crates/ironrdp-rdpsnd-native/Cargo.toml +++ b/crates/ironrdp-rdpsnd-native/Cargo.toml @@ -20,7 +20,7 @@ opus = ["dep:opus2", "dep:bytemuck"] [dependencies] anyhow = "1" -bytemuck = { version = "1.23", optional = true } +bytemuck = { version = "1.24", optional = true } cpal = "0.16" ironrdp-rdpsnd = { path = "../ironrdp-rdpsnd", version = "0.6" } # public opus2 = { version = "0.3", optional = true, features = ["bundled"] } From d24dbb1e2c580c2ca66c7d7a0cc3ce468e8e78a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Oct 2025 04:19:40 -0400 Subject: [PATCH 17/51] build(deps): bump tokio-tungstenite from 0.27.0 to 0.28.0 (#1009) --- Cargo.lock | 8 ++++---- crates/ironrdp-client/Cargo.toml | 2 +- crates/ironrdp-mstsgu/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7a3ffe06..1723af7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5493,9 +5493,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489a59b6730eda1b0171fcfda8b121f4bee2b35cba8645ca35c5f7ba3eb736c1" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" dependencies = [ "futures-util", "log", @@ -5723,9 +5723,9 @@ checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" [[package]] name = "tungstenite" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", diff --git a/crates/ironrdp-client/Cargo.toml b/crates/ironrdp-client/Cargo.toml index fab17993..4b4cffa8 100644 --- a/crates/ironrdp-client/Cargo.toml +++ b/crates/ironrdp-client/Cargo.toml @@ -72,7 +72,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } # Async, futures tokio = { version = "1", features = ["full"] } tokio-util = { version = "0.7" } -tokio-tungstenite = "0.27" +tokio-tungstenite = "0.28" transport = { git = "https://github.com/Devolutions/devolutions-gateway", rev = "06e91dfe82751a6502eaf74b6a99663f06f0236d" } futures-util = { version = "0.3", features = ["sink"] } diff --git a/crates/ironrdp-mstsgu/Cargo.toml b/crates/ironrdp-mstsgu/Cargo.toml index fdd83edb..988fb3a4 100644 --- a/crates/ironrdp-mstsgu/Cargo.toml +++ b/crates/ironrdp-mstsgu/Cargo.toml @@ -32,7 +32,7 @@ ironrdp-core = { path = "../ironrdp-core", version = "0.1", features = ["std"] } ironrdp-error = { path = "../ironrdp-error", version = "0.1" } ironrdp-tls = { path = "../ironrdp-tls", version = "0.1" } log = "0.4" -tokio-tungstenite = { version = "0.27" } +tokio-tungstenite = { version = "0.28" } tokio-util = { version = "0.7" } tokio = { version = "1.43", features = ["macros", "rt"] } uuid = { version = "1.16", features = ["v4"] } From a0a3e750c9e4ee9c73b957fbcb26dbc59e57d07d Mon Sep 17 00:00:00 2001 From: rhammonds-teleport Date: Thu, 9 Oct 2025 13:25:08 -0400 Subject: [PATCH 18/51] fix(rdpdr): fix incorrect padding when parsing NDR strings (#1015) When parsing Network Data Representation (NDR) messages, we're supposed to account for padding at the end of strings to remain aligned on a 4-byte boundary. The existing code doesn't seem to cover all cases, and the resulting misalignment causes misleading errors when processing the rest of the message. --- crates/ironrdp-rdpdr/src/pdu/esc/ndr.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/crates/ironrdp-rdpdr/src/pdu/esc/ndr.rs b/crates/ironrdp-rdpdr/src/pdu/esc/ndr.rs index 65afde51..8bc4e3e2 100644 --- a/crates/ironrdp-rdpdr/src/pdu/esc/ndr.rs +++ b/crates/ironrdp-rdpdr/src/pdu/esc/ndr.rs @@ -80,17 +80,21 @@ pub fn ptr_size(with_length: bool) -> usize { /// offset fields prefixing the string, as well as any extra padding for a 4-byte aligned /// NULL-terminated string. pub fn read_string_from_cursor(cursor: &mut ReadCursor<'_>, charset: CharacterSet) -> DecodeResult { + const ALIGNMENT: usize = 4; ensure_size!(ctx: "ndr::read_string_from_cursor", in: cursor, size: size_of::() * 3); - let length = cursor.read_u32(); + let _length = cursor.read_u32(); let _offset = cursor.read_u32(); let _length2 = cursor.read_u32(); let string = utils::read_string_from_cursor(cursor, charset, true)?; // Skip padding for 4-byte aligned NULL-terminated string. - if length % 2 != 0 { - ensure_size!(ctx: "ndr::read_string_from_cursor", in: cursor, size: size_of::()); - let _padding = cursor.read_u16(); + let mut pad = cursor.pos(); + let size = (pad + ALIGNMENT - 1) & !(ALIGNMENT - 1); + pad = size - pad; + if pad > 0 { + ensure_size!(ctx: "ndr::read_string_from_cursor", in: cursor, size: pad); + cursor.advance(pad); } Ok(string) From 6214c95c6f5504d995e0d3363d44981cef943bba Mon Sep 17 00:00:00 2001 From: Alex Yusiuk <55661041+RRRadicalEdward@users.noreply.github.com> Date: Tue, 14 Oct 2025 12:11:58 +0300 Subject: [PATCH 19/51] test(extra): move the `mod.rs` to the correct `tests` directory (#1019) --- crates/ironrdp-testsuite-extra/tests/{tests => }/mod.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename crates/ironrdp-testsuite-extra/tests/{tests => }/mod.rs (100%) diff --git a/crates/ironrdp-testsuite-extra/tests/tests/mod.rs b/crates/ironrdp-testsuite-extra/tests/mod.rs similarity index 100% rename from crates/ironrdp-testsuite-extra/tests/tests/mod.rs rename to crates/ironrdp-testsuite-extra/tests/mod.rs From 52225cca3eac12489bce4ca445b62e0c6fffe66d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 05:25:35 -0400 Subject: [PATCH 20/51] build(deps): bump the patch group across 1 directory with 3 updates (#1017) --- Cargo.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1723af7d..2e144e94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -708,9 +708,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.48" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" +checksum = "f4512b90fa68d3a9932cea5184017c5d200f5921df706d45e853537dea51508f" dependencies = [ "clap_builder", "clap_derive", @@ -718,9 +718,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.48" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" +checksum = "0025e98baa12e766c67ba13ff4695a887a1eba19569aad00a472546795bd6730" dependencies = [ "anstream", "anstyle", @@ -730,9 +730,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.47" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", "proc-macro2", @@ -3712,9 +3712,9 @@ dependencies = [ [[package]] name = "opus2" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e79f6e5198dfc9ec913fd4ddc8b53b87263d59c500b989f8449bd566552ce3" +checksum = "c3a4200c196ebf402d8a7091ce21845e8f2cd2f9c0c00deb73aaa14d024f6e6e" dependencies = [ "libopus_sys", ] @@ -4440,9 +4440,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "reqwest" -version = "0.12.23" +version = "0.12.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" dependencies = [ "base64", "bytes", From e87048c19b776e164349e4ffce880cfc29bddc61 Mon Sep 17 00:00:00 2001 From: Alex Yusiuk <55661041+RRRadicalEdward@users.noreply.github.com> Date: Mon, 20 Oct 2025 22:07:19 +0300 Subject: [PATCH 21/51] refactor: get rid of `lazy_static` (#1022) --- Cargo.lock | 3 - Cargo.toml | 1 - crates/ironrdp-graphics/Cargo.toml | 1 - .../src/rectangle_processing.rs | 146 +++--- .../src/zgfx/control_messages.rs | 21 +- crates/ironrdp-graphics/src/zgfx/mod.rs | 9 +- crates/ironrdp-pdu/Cargo.toml | 1 - .../src/basic_output/bitmap/tests.rs | 51 ++- .../src/basic_output/fast_path/tests.rs | 19 +- .../basic_output/surface_commands/tests.rs | 11 +- .../src/rdp/capability_sets/bitmap/tests.rs | 26 +- .../rdp/capability_sets/bitmap_cache/tests.rs | 113 +++-- .../capability_sets/bitmap_codecs/tests.rs | 124 +++--- .../src/rdp/capability_sets/brush/tests.rs | 11 +- .../src/rdp/capability_sets/general/tests.rs | 29 +- .../rdp/capability_sets/glyph_cache/tests.rs | 107 +++-- .../src/rdp/capability_sets/input/tests.rs | 21 +- .../offscreen_bitmap_cache/tests.rs | 16 +- .../src/rdp/capability_sets/order/tests.rs | 67 ++- .../src/rdp/capability_sets/pointer/tests.rs | 13 +- .../src/rdp/capability_sets/sound/tests.rs | 11 +- .../capability_sets/surface_commands/tests.rs | 11 +- .../capability_sets/virtual_channel/mod.rs | 2 +- .../capability_sets/virtual_channel/tests.rs | 25 +- .../client_new_license_request/tests.rs | 415 ++++++++++-------- .../test.rs | 59 +-- .../licensing_error_message/test.rs | 37 +- .../server_license_request/tests.rs | 101 +++-- .../server_platform_challenge/test.rs | 11 +- .../server_upgrade_license/tests.rs | 27 +- .../src/rdp/server_license/tests.rs | 23 +- .../ironrdp-pdu/src/rdp/session_info/tests.rs | 67 ++- crates/ironrdp-pdu/src/rdp/vc/tests.rs | 13 +- crates/ironrdp-testsuite-core/Cargo.toml | 1 - crates/ironrdp-testsuite-core/src/capsets.rs | 110 ++--- .../ironrdp-testsuite-core/src/client_info.rs | 138 +++--- .../src/cluster_data.rs | 15 +- .../src/conference_create.rs | 16 +- .../ironrdp-testsuite-core/src/core_data.rs | 147 ++++--- crates/ironrdp-testsuite-core/src/gcc.rs | 73 ++- crates/ironrdp-testsuite-core/src/gfx.rs | 19 +- .../src/graphics_messages.rs | 404 ++++++++--------- crates/ironrdp-testsuite-core/src/mcs.rs | 104 +++-- .../src/monitor_data.rs | 44 +- .../src/monitor_extended_data.rs | 21 +- .../src/multi_transport_channel_data.rs | 10 +- .../src/network_data.rs | 62 +-- crates/ironrdp-testsuite-core/src/rdp.rs | 263 ++++++----- .../src/security_data.rs | 43 +- .../ironrdp-testsuite-core/tests/pdu/input.rs | 25 +- .../ironrdp-testsuite-core/tests/pdu/rfx.rs | 26 +- fuzz/Cargo.lock | 7 - 52 files changed, 1569 insertions(+), 1551 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e144e94..38985bc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2597,7 +2597,6 @@ dependencies = [ "expect-test", "ironrdp-core", "ironrdp-pdu", - "lazy_static", "num-derive", "num-traits", "yuv", @@ -2643,7 +2642,6 @@ dependencies = [ "expect-test", "ironrdp-core", "ironrdp-error", - "lazy_static", "md-5", "num-bigint", "num-derive", @@ -2812,7 +2810,6 @@ dependencies = [ "ironrdp-rdpfile", "ironrdp-rdpsnd", "ironrdp-session", - "lazy_static", "paste", "png", "pretty_assertions", diff --git a/Cargo.toml b/Cargo.toml index 14037b6b..77859972 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,6 @@ rstest = "0.25" # Note: we are trying to move away from using these crates. # They are being kept around for now for legacy compatibility, # but new usage should be avoided. -lazy_static = "1.4" # Legacy crate; prefer std::sync::LazyLock or LazyCell num-derive = "0.4" num-traits = "0.2" diff --git a/crates/ironrdp-graphics/Cargo.toml b/crates/ironrdp-graphics/Cargo.toml index 4111a181..0f2f4f2c 100644 --- a/crates/ironrdp-graphics/Cargo.toml +++ b/crates/ironrdp-graphics/Cargo.toml @@ -22,7 +22,6 @@ bitvec = "1.0" ironrdp-core = { path = "../ironrdp-core", version = "0.1" } # public ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6", features = ["std"] } # public byteorder = "1.5" # TODO: remove -lazy_static.workspace = true # Legacy crate; prefer std::sync::LazyLock or LazyCell num-derive.workspace = true # TODO: remove num-traits.workspace = true # TODO: remove yuv = { version = "0.8", features = ["rdp"] } diff --git a/crates/ironrdp-graphics/src/rectangle_processing.rs b/crates/ironrdp-graphics/src/rectangle_processing.rs index 53d69171..eaffb39e 100644 --- a/crates/ironrdp-graphics/src/rectangle_processing.rs +++ b/crates/ironrdp-graphics/src/rectangle_processing.rs @@ -400,88 +400,86 @@ fn bands_internals_equal(first_band: &[InclusiveRectangle], second_band: &[Inclu #[cfg(test)] mod tests { - use lazy_static::lazy_static; + use std::sync::LazyLock; use super::*; - lazy_static! { - static ref REGION_FOR_RECTANGLES_INTERSECTION: Region = Region { - extents: InclusiveRectangle { + static REGION_FOR_RECTANGLES_INTERSECTION: LazyLock = LazyLock::new(|| Region { + extents: InclusiveRectangle { + left: 1, + top: 1, + right: 11, + bottom: 9, + }, + rectangles: vec![ + InclusiveRectangle { left: 1, top: 1, + right: 5, + bottom: 3, + }, + InclusiveRectangle { + left: 7, + top: 1, + right: 8, + bottom: 3, + }, + InclusiveRectangle { + left: 9, + top: 1, right: 11, + bottom: 3, + }, + InclusiveRectangle { + left: 7, + top: 3, + right: 11, + bottom: 4, + }, + InclusiveRectangle { + left: 3, + top: 4, + right: 6, + bottom: 6, + }, + InclusiveRectangle { + left: 7, + top: 4, + right: 11, + bottom: 6, + }, + InclusiveRectangle { + left: 1, + top: 6, + right: 3, + bottom: 8, + }, + InclusiveRectangle { + left: 4, + top: 6, + right: 5, + bottom: 8, + }, + InclusiveRectangle { + left: 6, + top: 6, + right: 10, + bottom: 8, + }, + InclusiveRectangle { + left: 4, + top: 8, + right: 5, bottom: 9, }, - rectangles: vec![ - InclusiveRectangle { - left: 1, - top: 1, - right: 5, - bottom: 3, - }, - InclusiveRectangle { - left: 7, - top: 1, - right: 8, - bottom: 3, - }, - InclusiveRectangle { - left: 9, - top: 1, - right: 11, - bottom: 3, - }, - InclusiveRectangle { - left: 7, - top: 3, - right: 11, - bottom: 4, - }, - InclusiveRectangle { - left: 3, - top: 4, - right: 6, - bottom: 6, - }, - InclusiveRectangle { - left: 7, - top: 4, - right: 11, - bottom: 6, - }, - InclusiveRectangle { - left: 1, - top: 6, - right: 3, - bottom: 8, - }, - InclusiveRectangle { - left: 4, - top: 6, - right: 5, - bottom: 8, - }, - InclusiveRectangle { - left: 6, - top: 6, - right: 10, - bottom: 8, - }, - InclusiveRectangle { - left: 4, - top: 8, - right: 5, - bottom: 9, - }, - InclusiveRectangle { - left: 6, - top: 8, - right: 10, - bottom: 9, - }, - ], - }; - } + InclusiveRectangle { + left: 6, + top: 8, + right: 10, + bottom: 9, + }, + ], + }); #[test] fn union_rectangle_sets_extents_and_single_rectangle_for_empty_region() { diff --git a/crates/ironrdp-graphics/src/zgfx/control_messages.rs b/crates/ironrdp-graphics/src/zgfx/control_messages.rs index 52231929..b6484419 100644 --- a/crates/ironrdp-graphics/src/zgfx/control_messages.rs +++ b/crates/ironrdp-graphics/src/zgfx/control_messages.rs @@ -84,7 +84,7 @@ bitflags! { #[cfg(test)] mod test { - use lazy_static::lazy_static; + use std::sync::LazyLock; use super::*; @@ -111,29 +111,30 @@ mod test { 0x02, // the third segment: data ]; - lazy_static! { - static ref SINGLE_SEGMENTED_DATA_PDU: SegmentedDataPdu<'static> = SegmentedDataPdu::Single(BulkEncodedData { + static SINGLE_SEGMENTED_DATA_PDU: LazyLock> = LazyLock::new(|| { + SegmentedDataPdu::Single(BulkEncodedData { compression_flags: CompressionFlags::COMPRESSED, data: &SINGLE_SEGMENTED_DATA_PDU_BUFFER[2..], - }); - static ref MULTIPART_SEGMENTED_DATA_PDU: SegmentedDataPdu<'static> = SegmentedDataPdu::Multipart { + }) + }); + static MULTIPART_SEGMENTED_DATA_PDU: LazyLock> = + LazyLock::new(|| SegmentedDataPdu::Multipart { uncompressed_size: 0x2B, segments: vec![ BulkEncodedData { compression_flags: CompressionFlags::empty(), - data: &MULTIPART_SEGMENTED_DATA_PDU_BUFFER[12..12 + 16] + data: &MULTIPART_SEGMENTED_DATA_PDU_BUFFER[12..12 + 16], }, BulkEncodedData { compression_flags: CompressionFlags::empty(), - data: &MULTIPART_SEGMENTED_DATA_PDU_BUFFER[33..33 + 13] + data: &MULTIPART_SEGMENTED_DATA_PDU_BUFFER[33..33 + 13], }, BulkEncodedData { compression_flags: CompressionFlags::COMPRESSED, - data: &MULTIPART_SEGMENTED_DATA_PDU_BUFFER[51..] + data: &MULTIPART_SEGMENTED_DATA_PDU_BUFFER[51..], }, ], - }; - } + }); #[test] fn from_buffer_correctly_parses_zgfx_single_segmented_data_pdu() { diff --git a/crates/ironrdp-graphics/src/zgfx/mod.rs b/crates/ironrdp-graphics/src/zgfx/mod.rs index 90aaa3a2..daa3cb4e 100644 --- a/crates/ironrdp-graphics/src/zgfx/mod.rs +++ b/crates/ironrdp-graphics/src/zgfx/mod.rs @@ -4,6 +4,7 @@ mod circular_buffer; mod control_messages; use std::io::{self, Write as _}; +use std::sync::LazyLock; use bitvec::bits; use bitvec::field::BitField as _; @@ -223,8 +224,8 @@ enum TokenType { }, } -lazy_static::lazy_static! { - static ref TOKEN_TABLE: [Token; 40] = [ +static TOKEN_TABLE: LazyLock<[Token; 40]> = LazyLock::new(|| { + [ Token { prefix: bits![static u8, Msb0; 0], ty: TokenType::NullLiteral, @@ -427,8 +428,8 @@ lazy_static::lazy_static! { distance_base: 17_094_304, }, }, - ]; -} + ] +}); #[derive(Debug)] pub enum ZgfxError { diff --git a/crates/ironrdp-pdu/Cargo.toml b/crates/ironrdp-pdu/Cargo.toml index ff8a9d94..cbe31ad8 100644 --- a/crates/ironrdp-pdu/Cargo.toml +++ b/crates/ironrdp-pdu/Cargo.toml @@ -44,7 +44,6 @@ pkcs1 = "0.7" [dev-dependencies] expect-test.workspace = true -lazy_static.workspace = true # TODO: remove in favor of https://doc.rust-lang.org/std/sync/struct.OnceLock.html [lints] workspace = true diff --git a/crates/ironrdp-pdu/src/basic_output/bitmap/tests.rs b/crates/ironrdp-pdu/src/basic_output/bitmap/tests.rs index 5adfa030..db490f4b 100644 --- a/crates/ironrdp-pdu/src/basic_output/bitmap/tests.rs +++ b/crates/ironrdp-pdu/src/basic_output/bitmap/tests.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode}; -use lazy_static::lazy_static; use super::*; @@ -31,31 +32,29 @@ const BITMAP_BUFFER: [u8; 114] = [ 0x55, 0xad, 0x10, 0x10, 0xa8, 0xd8, 0x60, 0x12, ]; -lazy_static! { - static ref BITMAP: BitmapUpdateData<'static> = BitmapUpdateData { - rectangles: { - let vec = vec![BitmapData { - rectangle: InclusiveRectangle { - left: 1792, - top: 1024, - right: 1855, - bottom: 1079, - }, - width: 64, - height: 56, - bits_per_pixel: 16, - compression_flags: Compression::BITMAP_COMPRESSION, - compressed_data_header: Some(CompressedDataHeader { - main_body_size: 80, - scan_width: 28, - uncompressed_size: 4, - }), - bitmap_data: &BITMAP_BUFFER[30..], - }]; - vec - } - }; -} +static BITMAP: LazyLock> = LazyLock::new(|| BitmapUpdateData { + rectangles: { + let vec = vec![BitmapData { + rectangle: InclusiveRectangle { + left: 1792, + top: 1024, + right: 1855, + bottom: 1079, + }, + width: 64, + height: 56, + bits_per_pixel: 16, + compression_flags: Compression::BITMAP_COMPRESSION, + compressed_data_header: Some(CompressedDataHeader { + main_body_size: 80, + scan_width: 28, + uncompressed_size: 4, + }), + bitmap_data: &BITMAP_BUFFER[30..], + }]; + vec + }, +}); #[test] fn from_buffer_bitmap_data_parsses_correctly() { diff --git a/crates/ironrdp-pdu/src/basic_output/fast_path/tests.rs b/crates/ironrdp-pdu/src/basic_output/fast_path/tests.rs index 5885aa2d..04c84d2d 100644 --- a/crates/ironrdp-pdu/src/basic_output/fast_path/tests.rs +++ b/crates/ironrdp-pdu/src/basic_output/fast_path/tests.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode}; -use lazy_static::lazy_static; use super::*; @@ -29,15 +30,13 @@ const FAST_PATH_HEADER_WITH_FORCED_LONG_LEN_PDU: FastPathHeader = FastPathHeader forced_long_length: true, }; -lazy_static! { - static ref FAST_PATH_UPDATE_PDU: FastPathUpdatePdu<'static> = FastPathUpdatePdu { - fragmentation: Fragmentation::Single, - update_code: UpdateCode::SurfaceCommands, - compression_flags: None, - compression_type: None, - data: &FAST_PATH_UPDATE_PDU_BUFFER[3..], - }; -} +static FAST_PATH_UPDATE_PDU: LazyLock> = LazyLock::new(|| FastPathUpdatePdu { + fragmentation: Fragmentation::Single, + update_code: UpdateCode::SurfaceCommands, + compression_flags: None, + compression_type: None, + data: &FAST_PATH_UPDATE_PDU_BUFFER[3..], +}); #[test] fn from_buffer_correctly_parses_fast_path_header_with_short_length() { diff --git a/crates/ironrdp-pdu/src/basic_output/surface_commands/tests.rs b/crates/ironrdp-pdu/src/basic_output/surface_commands/tests.rs index fd2e6830..db1db71b 100644 --- a/crates/ironrdp-pdu/src/basic_output/surface_commands/tests.rs +++ b/crates/ironrdp-pdu/src/basic_output/surface_commands/tests.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode}; -use lazy_static::lazy_static; use super::*; @@ -75,8 +76,8 @@ const FRAME_MARKER_PDU: SurfaceCommand<'_> = SurfaceCommand::FrameMarker(FrameMa frame_id: Some(5), }); -lazy_static! { - static ref SURFACE_BITS_PDU: SurfaceCommand<'static> = SurfaceCommand::StreamSurfaceBits(SurfaceBitsPdu { +static SURFACE_BITS_PDU: LazyLock> = LazyLock::new(|| { + SurfaceCommand::StreamSurfaceBits(SurfaceBitsPdu { destination: ExclusiveRectangle { left: 0, top: 0, @@ -91,8 +92,8 @@ lazy_static! { header: None, data: &SURFACE_BITS_BUFFER[22..], }, - }); -} + }) +}); #[test] fn from_buffer_correctly_parses_surface_command_frame_marker() { diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap/tests.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap/tests.rs index a1ba6b06..78b8fa10 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap/tests.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::*; @@ -19,28 +20,25 @@ const BITMAP_BUFFER: [u8; 24] = [ 0x00, 0x00, // pad2octetsB ]; -lazy_static! { - pub static ref BITMAP: Bitmap = Bitmap { - pref_bits_per_pix: 24, - desktop_width: 1280, - desktop_height: 1024, - desktop_resize_flag: true, - drawing_flags: BitmapDrawingFlags::ALLOW_SKIP_ALPHA, - }; -} +static BITMAP: LazyLock = LazyLock::new(|| Bitmap { + pref_bits_per_pix: 24, + desktop_width: 1280, + desktop_height: 1024, + desktop_resize_flag: true, + drawing_flags: BitmapDrawingFlags::ALLOW_SKIP_ALPHA, +}); #[test] fn from_buffer_correctly_parses_bitmap_capset() { let buffer = BITMAP_BUFFER.as_ref(); - assert_eq!(*BITMAP, decode(buffer).unwrap()); + let bitmap = LazyLock::force(&BITMAP); + assert_eq!(bitmap, &decode(buffer).unwrap()); } #[test] fn to_buffer_correctly_serializes_bitmap_capset() { - let capset = BITMAP.clone(); - - let buffer = encode_vec(&capset).unwrap(); + let buffer = encode_vec(LazyLock::force(&BITMAP)).unwrap(); assert_eq!(buffer, BITMAP_BUFFER.as_ref()); } diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_cache/tests.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_cache/tests.rs index 6ee14e9e..abd58ab8 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_cache/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_cache/tests.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::*; @@ -35,58 +36,56 @@ const BITMAP_CACHE_REV2_BUFFER: [u8; 36] = [ const CELL_INFO_BUFFER: [u8; 4] = [0xfb, 0x09, 0x00, 0x80]; -lazy_static! { - pub static ref BITMAP_CACHE: BitmapCache = BitmapCache { - caches: [ - CacheEntry { - entries: 200, - max_cell_size: 512 - }, - CacheEntry { - entries: 600, - max_cell_size: 2048 - }, - CacheEntry { - entries: 1000, - max_cell_size: 8192 - } - ], - }; - pub static ref BITMAP_CACHE_REV2: BitmapCacheRev2 = BitmapCacheRev2 { - cache_flags: CacheFlags::PERSISTENT_KEYS_EXPECTED_FLAG | CacheFlags::ALLOW_CACHE_WAITING_LIST_FLAG, - num_cell_caches: 3, - cache_cell_info: [ - CellInfo { - num_entries: 120, - is_cache_persistent: false - }, - CellInfo { - num_entries: 120, - is_cache_persistent: false - }, - CellInfo { - num_entries: 2555, - is_cache_persistent: true - }, - CellInfo { - num_entries: 0, - is_cache_persistent: false - }, - CellInfo { - num_entries: 0, - is_cache_persistent: false - } - ], - }; - pub static ref CELL_INFO: CellInfo = CellInfo { - num_entries: 2555, - is_cache_persistent: true - }; - pub static ref CACHE_ENTRY: CacheEntry = CacheEntry { - entries: 0x64, - max_cell_size: 0x32, - }; -} +static BITMAP_CACHE: LazyLock = LazyLock::new(|| BitmapCache { + caches: [ + CacheEntry { + entries: 200, + max_cell_size: 512, + }, + CacheEntry { + entries: 600, + max_cell_size: 2048, + }, + CacheEntry { + entries: 1000, + max_cell_size: 8192, + }, + ], +}); +static BITMAP_CACHE_REV2: LazyLock = LazyLock::new(|| BitmapCacheRev2 { + cache_flags: CacheFlags::PERSISTENT_KEYS_EXPECTED_FLAG | CacheFlags::ALLOW_CACHE_WAITING_LIST_FLAG, + num_cell_caches: 3, + cache_cell_info: [ + CellInfo { + num_entries: 120, + is_cache_persistent: false, + }, + CellInfo { + num_entries: 120, + is_cache_persistent: false, + }, + CellInfo { + num_entries: 2555, + is_cache_persistent: true, + }, + CellInfo { + num_entries: 0, + is_cache_persistent: false, + }, + CellInfo { + num_entries: 0, + is_cache_persistent: false, + }, + ], +}); +static CELL_INFO: LazyLock = LazyLock::new(|| CellInfo { + num_entries: 2555, + is_cache_persistent: true, +}); +static CACHE_ENTRY: LazyLock = LazyLock::new(|| CacheEntry { + entries: 0x64, + max_cell_size: 0x32, +}); #[test] fn from_buffer_correctly_parses_bitmap_cache_capset() { @@ -97,9 +96,7 @@ fn from_buffer_correctly_parses_bitmap_cache_capset() { #[test] fn to_buffer_correctly_serializes_bitmap_cache_capset() { - let bitmap_cache = BITMAP_CACHE.clone(); - - let buffer = encode_vec(&bitmap_cache).unwrap(); + let buffer = encode_vec(&*BITMAP_CACHE).unwrap(); assert_eq!(buffer, BITMAP_CACHE_BUFFER.as_ref()); } @@ -118,9 +115,7 @@ fn from_buffer_correctly_parses_bitmap_cache_rev2_capset() { #[test] fn to_buffer_correctly_serializes_bitmap_cache_rev2_capset() { - let bitmap_cache = BITMAP_CACHE_REV2.clone(); - - let buffer = encode_vec(&bitmap_cache).unwrap(); + let buffer = encode_vec(&*BITMAP_CACHE_REV2).unwrap(); assert_eq!(buffer, BITMAP_CACHE_REV2_BUFFER.as_ref()); } diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs/tests.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs/tests.rs index e9055f25..0828f2d6 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs/tests.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode_cursor, encode_vec, DecodeErrorKind}; -use lazy_static::lazy_static; use super::*; @@ -168,14 +169,27 @@ const BITMAP_CODECS_BUFFER: [u8; 91] = [ 0x03, // color loss level ]; -lazy_static! { - #[rustfmt::skip] - pub static ref GUID: Guid = Guid(0xca8d_1bb9, 0x000f, 0x154f, 0x58, 0x9f, 0xae, 0x2d, 0x1a, 0x87, 0xe2, 0xd6); - pub static ref RFX_ICAP: RfxICap = RfxICap { - flags: RfxICapFlags::CODEC_MODE, - entropy_bits: EntropyBits::Rlgr3, - }; - pub static ref RFX_CAPSET: RfxCapset = RfxCapset(vec![ +static GUID: LazyLock = LazyLock::new(|| { + Guid( + 0xca8d_1bb9, + 0x000f, + 0x154f, + 0x58, + 0x9f, + 0xae, + 0x2d, + 0x1a, + 0x87, + 0xe2, + 0xd6, + ) +}); +static RFX_ICAP: LazyLock = LazyLock::new(|| RfxICap { + flags: RfxICapFlags::CODEC_MODE, + entropy_bits: EntropyBits::Rlgr3, +}); +static RFX_CAPSET: LazyLock = LazyLock::new(|| { + RfxCapset(vec![ RfxICap { flags: RfxICapFlags::empty(), entropy_bits: EntropyBits::Rlgr1, @@ -183,9 +197,11 @@ lazy_static! { RfxICap { flags: RfxICapFlags::CODEC_MODE, entropy_bits: EntropyBits::Rlgr3, - } - ]); - pub static ref RFX_CAPS: RfxCaps = RfxCaps(RfxCapset(vec![ + }, + ]) +}); +static RFX_CAPS: LazyLock = LazyLock::new(|| { + RfxCaps(RfxCapset(vec![ RfxICap { flags: RfxICapFlags::empty(), entropy_bits: EntropyBits::Rlgr1, @@ -193,9 +209,30 @@ lazy_static! { RfxICap { flags: RfxICapFlags::CODEC_MODE, entropy_bits: EntropyBits::Rlgr3, - } - ])); - pub static ref RFX_CLIENT_CAPS_CONTAINER: RfxClientCapsContainer = RfxClientCapsContainer { + }, + ])) +}); +static RFX_CLIENT_CAPS_CONTAINER: LazyLock = LazyLock::new(|| RfxClientCapsContainer { + capture_flags: CaptureFlags::CARDP_CAPS_CAPTURE_NON_CAC, + caps_data: RfxCaps(RfxCapset(vec![ + RfxICap { + flags: RfxICapFlags::empty(), + entropy_bits: EntropyBits::Rlgr1, + }, + RfxICap { + flags: RfxICapFlags::CODEC_MODE, + entropy_bits: EntropyBits::Rlgr3, + }, + ])), +}); +static NSCODEC: LazyLock = LazyLock::new(|| NsCodec { + is_dynamic_fidelity_allowed: true, + is_subsampling_allowed: true, + color_loss_level: 3, +}); +static CODEC: LazyLock = LazyLock::new(|| Codec { + id: 3, + property: CodecProperty::RemoteFx(RemoteFxContainer::ClientContainer(RfxClientCapsContainer { capture_flags: CaptureFlags::CARDP_CAPS_CAPTURE_NON_CAC, caps_data: RfxCaps(RfxCapset(vec![ RfxICap { @@ -205,18 +242,19 @@ lazy_static! { RfxICap { flags: RfxICapFlags::CODEC_MODE, entropy_bits: EntropyBits::Rlgr3, - } + }, ])), - }; - pub static ref NSCODEC: NsCodec = NsCodec { - is_dynamic_fidelity_allowed: true, - is_subsampling_allowed: true, - color_loss_level: 3, - }; - pub static ref CODEC: Codec = Codec { - id: 3, - property: CodecProperty::RemoteFx(RemoteFxContainer::ClientContainer( - RfxClientCapsContainer { + })), +}); +static CODEC_SERVER_MODE: LazyLock = LazyLock::new(|| Codec { + id: 0, + property: CodecProperty::ImageRemoteFx(RemoteFxContainer::ServerContainer(4)), +}); +static BITMAP_CODECS: LazyLock = LazyLock::new(|| { + BitmapCodecs(vec![ + Codec { + id: 3, + property: CodecProperty::RemoteFx(RemoteFxContainer::ClientContainer(RfxClientCapsContainer { capture_flags: CaptureFlags::CARDP_CAPS_CAPTURE_NON_CAC, caps_data: RfxCaps(RfxCapset(vec![ RfxICap { @@ -226,33 +264,9 @@ lazy_static! { RfxICap { flags: RfxICapFlags::CODEC_MODE, entropy_bits: EntropyBits::Rlgr3, - } + }, ])), - } - )), - }; - pub static ref CODEC_SERVER_MODE: Codec = Codec { - id: 0, - property: CodecProperty::ImageRemoteFx(RemoteFxContainer::ServerContainer(4)), - }; - pub static ref BITMAP_CODECS: BitmapCodecs = BitmapCodecs(vec![ - Codec { - id: 3, - property: CodecProperty::RemoteFx(RemoteFxContainer::ClientContainer( - RfxClientCapsContainer { - capture_flags: CaptureFlags::CARDP_CAPS_CAPTURE_NON_CAC, - caps_data: RfxCaps(RfxCapset(vec![ - RfxICap { - flags: RfxICapFlags::empty(), - entropy_bits: EntropyBits::Rlgr1, - }, - RfxICap { - flags: RfxICapFlags::CODEC_MODE, - entropy_bits: EntropyBits::Rlgr3, - } - ])), - } - )) + })), }, Codec { id: 1, @@ -260,10 +274,10 @@ lazy_static! { is_dynamic_fidelity_allowed: true, is_subsampling_allowed: true, color_loss_level: 3, - }) + }), }, - ]); -} + ]) +}); #[test] fn from_buffer_correctly_parses_guid() { diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/brush/tests.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/brush/tests.rs index 3941e4f9..75d9612a 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/brush/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/brush/tests.rs @@ -1,15 +1,14 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::*; const BRUSH_BUFFER: [u8; 4] = [0x01, 0x00, 0x00, 0x00]; -lazy_static! { - pub static ref BRUSH: Brush = Brush { - support_level: SupportLevel::Color8x8, - }; -} +static BRUSH: LazyLock = LazyLock::new(|| Brush { + support_level: SupportLevel::Color8x8, +}); #[test] fn from_buffer_successfully_parses_brush_capset() { diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/general/tests.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/general/tests.rs index 76458295..55b09cbf 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/general/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/general/tests.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::*; @@ -17,20 +18,18 @@ const GENERAL_CAPSET_BUFFER: [u8; 20] = [ 0x00, // suppressOutputSupport ]; -lazy_static! { - pub static ref CAPSET_GENERAL: General = General { - major_platform_type: MajorPlatformType::WINDOWS, - minor_platform_type: MinorPlatformType::WINDOWS_NT, - protocol_version: PROTOCOL_VER, - extra_flags: GeneralExtraFlags::FASTPATH_OUTPUT_SUPPORTED - | GeneralExtraFlags::LONG_CREDENTIALS_SUPPORTED - | GeneralExtraFlags::AUTORECONNECT_SUPPORTED - | GeneralExtraFlags::ENC_SALTED_CHECKSUM - | GeneralExtraFlags::NO_BITMAP_COMPRESSION_HDR, - refresh_rect_support: false, - suppress_output_support: false, - }; -} +static CAPSET_GENERAL: LazyLock = LazyLock::new(|| General { + major_platform_type: MajorPlatformType::WINDOWS, + minor_platform_type: MinorPlatformType::WINDOWS_NT, + protocol_version: PROTOCOL_VER, + extra_flags: GeneralExtraFlags::FASTPATH_OUTPUT_SUPPORTED + | GeneralExtraFlags::LONG_CREDENTIALS_SUPPORTED + | GeneralExtraFlags::AUTORECONNECT_SUPPORTED + | GeneralExtraFlags::ENC_SALTED_CHECKSUM + | GeneralExtraFlags::NO_BITMAP_COMPRESSION_HDR, + refresh_rect_support: false, + suppress_output_support: false, +}); #[test] fn from_buffer_correctly_parses_general_capset() { diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/glyph_cache/tests.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/glyph_cache/tests.rs index be7443e0..fff93d06 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/glyph_cache/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/glyph_cache/tests.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::*; @@ -14,61 +15,59 @@ const GLYPH_CACHE_BUFFER: [u8; 48] = [ const CACHE_DEFINITION_BUFFER: [u8; 4] = [0xfe, 0x00, 0x04, 0x00]; -lazy_static! { - pub static ref GLYPH_CACHE: GlyphCache = GlyphCache { - glyph_cache: [ - CacheDefinition { - entries: 254, - max_cell_size: 4 - }, - CacheDefinition { - entries: 254, - max_cell_size: 4 - }, - CacheDefinition { - entries: 254, - max_cell_size: 8 - }, - CacheDefinition { - entries: 254, - max_cell_size: 8 - }, - CacheDefinition { - entries: 254, - max_cell_size: 16 - }, - CacheDefinition { - entries: 254, - max_cell_size: 32 - }, - CacheDefinition { - entries: 254, - max_cell_size: 64 - }, - CacheDefinition { - entries: 254, - max_cell_size: 128 - }, - CacheDefinition { - entries: 254, - max_cell_size: 256 - }, - CacheDefinition { - entries: 64, - max_cell_size: 2048 - } - ], - frag_cache: CacheDefinition { - entries: 256, +static GLYPH_CACHE: LazyLock = LazyLock::new(|| GlyphCache { + glyph_cache: [ + CacheDefinition { + entries: 254, + max_cell_size: 4, + }, + CacheDefinition { + entries: 254, + max_cell_size: 4, + }, + CacheDefinition { + entries: 254, + max_cell_size: 8, + }, + CacheDefinition { + entries: 254, + max_cell_size: 8, + }, + CacheDefinition { + entries: 254, + max_cell_size: 16, + }, + CacheDefinition { + entries: 254, + max_cell_size: 32, + }, + CacheDefinition { + entries: 254, + max_cell_size: 64, + }, + CacheDefinition { + entries: 254, + max_cell_size: 128, + }, + CacheDefinition { + entries: 254, max_cell_size: 256, }, - glyph_support_level: GlyphSupportLevel::Encode, - }; - pub static ref CACHE_DEFINITION: CacheDefinition = CacheDefinition { - entries: 254, - max_cell_size: 4, - }; -} + CacheDefinition { + entries: 64, + max_cell_size: 2048, + }, + ], + frag_cache: CacheDefinition { + entries: 256, + max_cell_size: 256, + }, + glyph_support_level: GlyphSupportLevel::Encode, +}); +static CACHE_DEFINITION: LazyLock = LazyLock::new(|| CacheDefinition { + entries: 254, + max_cell_size: 4, +}); #[test] fn from_buffer_correctly_parses_glyph_cache_capset() { diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/input/tests.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/input/tests.rs index a9d15011..5337365d 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/input/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/input/tests.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::*; @@ -16,16 +17,14 @@ const INPUT_BUFFER: [u8; 84] = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // imeFileName ]; -lazy_static! { - pub static ref INPUT: Input = Input { - input_flags: InputFlags::SCANCODES | InputFlags::UNICODE | InputFlags::MOUSEX, - keyboard_layout: 0x409, - keyboard_type: Some(KeyboardType::IbmEnhanced), - keyboard_subtype: 0, - keyboard_function_key: 12, - keyboard_ime_filename: String::new(), - }; -} +static INPUT: LazyLock = LazyLock::new(|| Input { + input_flags: InputFlags::SCANCODES | InputFlags::UNICODE | InputFlags::MOUSEX, + keyboard_layout: 0x409, + keyboard_type: Some(KeyboardType::IbmEnhanced), + keyboard_subtype: 0, + keyboard_function_key: 12, + keyboard_ime_filename: String::new(), +}); #[test] fn from_buffer_correctly_parses_input_capset() { diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/offscreen_bitmap_cache/tests.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/offscreen_bitmap_cache/tests.rs index 5501ac74..e8dac691 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/offscreen_bitmap_cache/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/offscreen_bitmap_cache/tests.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::*; @@ -8,14 +9,11 @@ const OFFSCREEN_BITMAP_CACHE_BUFFER: [u8; 8] = [ 0x00, 0x1e, // offscreenCacheSize 0x64, 0x00, // offscreenCacheEntries ]; - -lazy_static! { - pub static ref OFFSCREEN_BITMAP_CACHE: OffscreenBitmapCache = OffscreenBitmapCache { - is_supported: true, - cache_size: 7680, - cache_entries: 100, - }; -} +static OFFSCREEN_BITMAP_CACHE: LazyLock = LazyLock::new(|| OffscreenBitmapCache { + is_supported: true, + cache_size: 7680, + cache_entries: 100, +}); #[test] fn from_buffer_correctly_parses_offscreen_bitmap_cache_capset() { diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/order/tests.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/order/tests.rs index 995c3e4f..85d0e2ee 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/order/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/order/tests.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::*; @@ -24,42 +25,40 @@ const ORDER_BUFFER: [u8; 84] = [ 0x00, 0x00, // pad2octetsE ]; -lazy_static! { - pub static ref ORDER: Order = Order { - order_flags: OrderFlags::COLOR_INDEX_SUPPORT | OrderFlags::NEGOTIATE_ORDER_SUPPORT, - order_support: { - let mut array = [0u8; 32]; +static ORDER: LazyLock = LazyLock::new(|| Order { + order_flags: OrderFlags::COLOR_INDEX_SUPPORT | OrderFlags::NEGOTIATE_ORDER_SUPPORT, + order_support: { + let mut array = [0u8; 32]; - array[usize::from(OrderSupportIndex::DstBlt.as_u8())] = 1; - array[usize::from(OrderSupportIndex::PatBlt.as_u8())] = 1; - array[usize::from(OrderSupportIndex::ScrBlt.as_u8())] = 1; - array[usize::from(OrderSupportIndex::MemBlt.as_u8())] = 1; - array[usize::from(OrderSupportIndex::Mem3Blt.as_u8())] = 1; - array[usize::from(OrderSupportIndex::DrawnInEGrid.as_u8())] = 1; - array[usize::from(OrderSupportIndex::LineTo.as_u8())] = 1; - array[usize::from(OrderSupportIndex::MultiDrawnInEGrid.as_u8())] = 1; - array[usize::from(OrderSupportIndex::SaveBitmap.as_u8())] = 1; - array[usize::from(OrderSupportIndex::MultiDstBlt.as_u8())] = 1; - array[usize::from(OrderSupportIndex::MultiPatBlt.as_u8())] = 1; - array[usize::from(OrderSupportIndex::MultiScrBlt.as_u8())] = 1; - array[usize::from(OrderSupportIndex::MultiOpaqueRect.as_u8())] = 1; - array[usize::from(OrderSupportIndex::Fast.as_u8())] = 1; - array[usize::from(OrderSupportIndex::PolygonSC.as_u8())] = 1; - array[usize::from(OrderSupportIndex::PolygonCB.as_u8())] = 1; - array[usize::from(OrderSupportIndex::Polyline.as_u8())] = 1; - array[usize::from(OrderSupportIndex::FastGlyph.as_u8())] = 1; - array[usize::from(OrderSupportIndex::EllipseSC.as_u8())] = 1; - array[usize::from(OrderSupportIndex::EllipseCB.as_u8())] = 1; - array[usize::from(OrderSupportIndex::Index.as_u8())] = 1; + array[usize::from(OrderSupportIndex::DstBlt.as_u8())] = 1; + array[usize::from(OrderSupportIndex::PatBlt.as_u8())] = 1; + array[usize::from(OrderSupportIndex::ScrBlt.as_u8())] = 1; + array[usize::from(OrderSupportIndex::MemBlt.as_u8())] = 1; + array[usize::from(OrderSupportIndex::Mem3Blt.as_u8())] = 1; + array[usize::from(OrderSupportIndex::DrawnInEGrid.as_u8())] = 1; + array[usize::from(OrderSupportIndex::LineTo.as_u8())] = 1; + array[usize::from(OrderSupportIndex::MultiDrawnInEGrid.as_u8())] = 1; + array[usize::from(OrderSupportIndex::SaveBitmap.as_u8())] = 1; + array[usize::from(OrderSupportIndex::MultiDstBlt.as_u8())] = 1; + array[usize::from(OrderSupportIndex::MultiPatBlt.as_u8())] = 1; + array[usize::from(OrderSupportIndex::MultiScrBlt.as_u8())] = 1; + array[usize::from(OrderSupportIndex::MultiOpaqueRect.as_u8())] = 1; + array[usize::from(OrderSupportIndex::Fast.as_u8())] = 1; + array[usize::from(OrderSupportIndex::PolygonSC.as_u8())] = 1; + array[usize::from(OrderSupportIndex::PolygonCB.as_u8())] = 1; + array[usize::from(OrderSupportIndex::Polyline.as_u8())] = 1; + array[usize::from(OrderSupportIndex::FastGlyph.as_u8())] = 1; + array[usize::from(OrderSupportIndex::EllipseSC.as_u8())] = 1; + array[usize::from(OrderSupportIndex::EllipseCB.as_u8())] = 1; + array[usize::from(OrderSupportIndex::Index.as_u8())] = 1; - array - }, + array + }, - order_support_ex_flags: OrderSupportExFlags::CACHE_BITMAP_REV3_SUPPORT, - desktop_save_size: 230_400, - text_ansi_code_page: 0, - }; -} + order_support_ex_flags: OrderSupportExFlags::CACHE_BITMAP_REV3_SUPPORT, + desktop_save_size: 230_400, + text_ansi_code_page: 0, +}); #[test] fn from_buffer_correctly_parses_order_capset() { diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/pointer/tests.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/pointer/tests.rs index d5933ab9..f7b0a73a 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/pointer/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/pointer/tests.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::*; @@ -9,12 +10,10 @@ const POINTER_BUFFER: [u8; 6] = [ 0x15, 0x00, // pointerCacheSize ]; -lazy_static! { - pub static ref POINTER: Pointer = Pointer { - color_pointer_cache_size: 20, - pointer_cache_size: 21, - }; -} +static POINTER: LazyLock = LazyLock::new(|| Pointer { + color_pointer_cache_size: 20, + pointer_cache_size: 21, +}); #[test] fn from_buffer_correctly_parses_pointer_capset() { diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/sound/tests.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/sound/tests.rs index 0d3627ec..d43f52b0 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/sound/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/sound/tests.rs @@ -1,15 +1,14 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::*; const SOUND_BUFFER: [u8; 4] = [0x01, 0x00, 0x00, 0x00]; -lazy_static! { - pub static ref SOUND: Sound = Sound { - flags: SoundFlags::BEEPS, - }; -} +static SOUND: LazyLock = LazyLock::new(|| Sound { + flags: SoundFlags::BEEPS, +}); #[test] fn from_buffer_correctly_parses_sound_capset() { diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/surface_commands/tests.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/surface_commands/tests.rs index 1dfbb2c0..ac96f46e 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/surface_commands/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/surface_commands/tests.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::*; @@ -8,11 +9,9 @@ const SURFACE_COMMANDS_BUFFER: [u8; 8] = [ 0x00, 0x00, 0x00, 0x00, // reserved ]; -lazy_static! { - pub static ref SURFACE_COMMANDS: SurfaceCommands = SurfaceCommands { - flags: CmdFlags::SET_SURFACE_BITS | CmdFlags::FRAME_MARKER | CmdFlags::STREAM_SURFACE_BITS, - }; -} +static SURFACE_COMMANDS: LazyLock = LazyLock::new(|| SurfaceCommands { + flags: CmdFlags::SET_SURFACE_BITS | CmdFlags::FRAME_MARKER | CmdFlags::STREAM_SURFACE_BITS, +}); #[test] fn from_buffer_correctly_parses_surface_commands_capset() { diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/virtual_channel/mod.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/virtual_channel/mod.rs index 84e6ac42..085e78f9 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/virtual_channel/mod.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/virtual_channel/mod.rs @@ -29,7 +29,7 @@ bitflags! { /// # MSDN /// /// * [Virtual Channel Capability Set](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/a8593178-80c0-4b80-876c-cb77e62cecfc) -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct VirtualChannel { pub flags: VirtualChannelFlags, pub chunk_size: Option, diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/virtual_channel/tests.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/virtual_channel/tests.rs index 6f42c549..ff14c89a 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/virtual_channel/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/virtual_channel/tests.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::*; @@ -12,16 +13,14 @@ const VIRTUAL_CHANNEL_BUFFER: [u8; 8] = [ 0x40, 0x06, 0x00, 0x00, // chunk size ]; -lazy_static! { - pub static ref VIRTUAL_CHANNEL_INCOMPLETE: VirtualChannel = VirtualChannel { - flags: VirtualChannelFlags::COMPRESSION_SERVER_TO_CLIENT, - chunk_size: None, - }; - pub static ref VIRTUAL_CHANNEL: VirtualChannel = VirtualChannel { - flags: VirtualChannelFlags::NO_COMPRESSION, - chunk_size: Some(1600), - }; -} +static VIRTUAL_CHANNEL_INCOMPLETE: LazyLock = LazyLock::new(|| VirtualChannel { + flags: VirtualChannelFlags::COMPRESSION_SERVER_TO_CLIENT, + chunk_size: None, +}); +static VIRTUAL_CHANNEL: LazyLock = LazyLock::new(|| VirtualChannel { + flags: VirtualChannelFlags::NO_COMPRESSION, + chunk_size: Some(1600), +}); #[test] fn from_buffer_correctly_parses_virtual_channel_incomplete_capset() { @@ -38,7 +37,7 @@ fn from_buffer_correctly_parses_virtual_channel_capset() { #[test] fn to_buffer_correctly_serializes_virtual_channel_incomplete_capset() { - let c = VIRTUAL_CHANNEL_INCOMPLETE.clone(); + let c = *VIRTUAL_CHANNEL_INCOMPLETE; let buffer = encode_vec(&c).unwrap(); @@ -47,7 +46,7 @@ fn to_buffer_correctly_serializes_virtual_channel_incomplete_capset() { #[test] fn to_buffer_correctly_serializes_virtual_channel_capset() { - let c = VIRTUAL_CHANNEL.clone(); + let c = *VIRTUAL_CHANNEL; let buffer = encode_vec(&c).unwrap(); diff --git a/crates/ironrdp-pdu/src/rdp/server_license/client_new_license_request/tests.rs b/crates/ironrdp-pdu/src/rdp/server_license/client_new_license_request/tests.rs index 8cd33340..8d54c706 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/client_new_license_request/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/client_new_license_request/tests.rs @@ -1,6 +1,7 @@ +use std::sync::LazyLock; + use byteorder::{LittleEndian, WriteBytesExt as _}; use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::*; use crate::rdp::server_license::server_license_request::cert::{CertificateType, X509CertificateChain}; @@ -59,8 +60,8 @@ const LICENSE_KEY_BUFFER: [u8; 16] = [ const CLIENT_USERNAME: &str = "sample-user"; const CLIENT_MACHINE_NAME: &str = "sample-machine-name"; -lazy_static! { - pub static ref CLIENT_NEW_LICENSE_REQUEST: LicensePdu = ClientNewLicenseRequest { +static CLIENT_NEW_LICENSE_REQUEST: LazyLock = LazyLock::new(|| { + ClientNewLicenseRequest { license_header: LicenseHeader { security_header: BasicSecurityHeader { flags: BasicSecurityHeaderFlags::LICENSE_PKT, @@ -68,212 +69,238 @@ lazy_static! { preamble_message_type: PreambleType::NewLicenseRequest, preamble_flags: PreambleFlags::empty(), preamble_version: PreambleVersion::V3, - preamble_message_size: u16::try_from(PREAMBLE_SIZE - + RANDOM_NUMBER_SIZE - + LICENSE_REQUEST_STATIC_FIELDS_SIZE - + ENCRYPTED_PREMASTER_SECRET.len() - + CLIENT_MACHINE_NAME.len() - + UTF8_NULL_TERMINATOR_SIZE - + CLIENT_USERNAME.len() - + UTF8_NULL_TERMINATOR_SIZE).expect("can't panic"), + preamble_message_size: u16::try_from( + PREAMBLE_SIZE + + RANDOM_NUMBER_SIZE + + LICENSE_REQUEST_STATIC_FIELDS_SIZE + + ENCRYPTED_PREMASTER_SECRET.len() + + CLIENT_MACHINE_NAME.len() + + UTF8_NULL_TERMINATOR_SIZE + + CLIENT_USERNAME.len() + + UTF8_NULL_TERMINATOR_SIZE, + ) + .expect("can't panic"), }, client_random: Vec::from(CLIENT_RANDOM_BUFFER.as_ref()), encrypted_premaster_secret: Vec::from(ENCRYPTED_PREMASTER_SECRET.as_ref()), client_username: CLIENT_USERNAME.to_owned(), client_machine_name: CLIENT_MACHINE_NAME.to_owned(), - }.into(); + } + .into() +}); - pub static ref REQUEST_BUFFER: Vec = { - let username_len = CLIENT_USERNAME.len() + UTF8_NULL_TERMINATOR_SIZE; - let mut username_len_buf = Vec::new(); - username_len_buf.write_u16::(u16::try_from(username_len).expect("can't panic")).unwrap(); +static REQUEST_BUFFER: LazyLock> = LazyLock::new(|| { + let username_len = CLIENT_USERNAME.len() + UTF8_NULL_TERMINATOR_SIZE; + let mut username_len_buf = Vec::new(); + username_len_buf + .write_u16::(u16::try_from(username_len).expect("can't panic")) + .unwrap(); - let machine_name_len = CLIENT_MACHINE_NAME.len() + UTF8_NULL_TERMINATOR_SIZE; - let mut machine_name_len_buf = Vec::new(); - machine_name_len_buf.write_u16::(u16::try_from(machine_name_len).unwrap()).unwrap(); + let machine_name_len = CLIENT_MACHINE_NAME.len() + UTF8_NULL_TERMINATOR_SIZE; + let mut machine_name_len_buf = Vec::new(); + machine_name_len_buf + .write_u16::(u16::try_from(machine_name_len).unwrap()) + .unwrap(); - let buf = [ - &[0x01u8, 0x00, 0x00, 0x00, // preferred_key_exchange_algorithm - 0x00, 0x00, 0x01, 0x04], // platform_id - CLIENT_RANDOM_BUFFER.as_ref(), - &[0x02, 0x00, // blob type - 0x48, 0x00], // blob len - ENCRYPTED_PREMASTER_SECRET.as_ref(), - &[0x0f, 0x00], // blob type - username_len_buf.as_slice(), - CLIENT_USERNAME.as_bytes(), - &[0x00, // null - 0x10, 0x00], // blob type - machine_name_len_buf.as_slice(), // blob len - CLIENT_MACHINE_NAME.as_bytes(), - &[0x00]] // null - .concat(); + let buf = [ + &[ + 0x01u8, 0x00, 0x00, 0x00, // preferred_key_exchange_algorithm + 0x00, 0x00, 0x01, 0x04, + ], // platform_id + CLIENT_RANDOM_BUFFER.as_ref(), + &[ + 0x02, 0x00, // blob type + 0x48, 0x00, + ], // blob len + ENCRYPTED_PREMASTER_SECRET.as_ref(), + &[0x0f, 0x00], // blob type + username_len_buf.as_slice(), + CLIENT_USERNAME.as_bytes(), + &[ + 0x00, // null + 0x10, 0x00, + ], // blob type + machine_name_len_buf.as_slice(), // blob len + CLIENT_MACHINE_NAME.as_bytes(), + &[0x00], + ] // null + .concat(); - let preamble_size_field = u16::try_from(buf.len() + PREAMBLE_SIZE).expect("can't panic"); + let preamble_size_field = u16::try_from(buf.len() + PREAMBLE_SIZE).expect("can't panic"); - [ - LICENSE_HEADER_BUFFER_NO_SIZE.as_ref(), - &preamble_size_field.to_le_bytes(), - buf.as_slice() - ] - .concat() - }; + [ + LICENSE_HEADER_BUFFER_NO_SIZE.as_ref(), + &preamble_size_field.to_le_bytes(), + buf.as_slice(), + ] + .concat() +}); - pub(crate) static ref SERVER_LICENSE_REQUEST: LicensePdu = { - let mut req = ServerLicenseRequest { - license_header: LicenseHeader { - security_header: BasicSecurityHeader { - flags: BasicSecurityHeaderFlags::LICENSE_PKT, - }, - preamble_message_type: PreambleType::LicenseRequest, - preamble_flags: PreambleFlags::empty(), - preamble_version: PreambleVersion::V3, - preamble_message_size: 0, +pub(crate) static SERVER_LICENSE_REQUEST: LazyLock = LazyLock::new(|| { + let mut req = ServerLicenseRequest { + license_header: LicenseHeader { + security_header: BasicSecurityHeader { + flags: BasicSecurityHeaderFlags::LICENSE_PKT, }, - server_random: Vec::from(SERVER_RANDOM_BUFFER.as_ref()), - product_info: ProductInfo { - version: 0x60000, - company_name: "Microsoft Corporation".to_owned(), - product_id: "A02".to_owned(), - }, - server_certificate: Some(ServerCertificate { - issued_permanently: true, - certificate: CertificateType::X509(X509CertificateChain { - certificate_array: vec![ - vec![0x30, 0x82, 0x03, 0xda, 0x30, 0x82, 0x02, 0xc2, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x13, 0x7f, - 0x00, 0x00, 0x01, 0x76, 0x00, 0x8f, 0x08, 0x64, 0x08, 0x68, 0xa7, 0x63, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x76, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, - 0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x50, 0x72, 0x6f, 0x64, 0x32, - 0x4c, 0x53, 0x52, 0x41, 0x73, 0x68, 0x61, 0x32, 0x52, 0x44, 0x53, 0x4c, 0x4d, 0x30, 0x1e, 0x17, 0x0d, - 0x31, 0x39, 0x31, 0x30, 0x32, 0x36, 0x32, 0x32, 0x35, 0x33, 0x34, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x37, - 0x30, 0x36, 0x30, 0x36, 0x32, 0x30, 0x34, 0x32, 0x33, 0x38, 0x5a, 0x30, 0x11, 0x31, 0x0f, 0x30, 0x0d, - 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x06, 0x42, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x30, 0x82, 0x01, 0x22, - 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, - 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa8, 0x6b, 0xda, 0xae, 0x08, - 0x1d, 0xc5, 0x05, 0x70, 0x7d, 0xa0, 0x41, 0x46, 0xb4, 0x14, 0xcf, 0xfb, 0x8e, 0x09, 0x0b, 0x0a, 0x52, - 0x8a, 0x7f, 0x7a, 0x35, 0xb6, 0xe3, 0x0d, 0x1c, 0xbe, 0x49, 0x63, 0x41, 0x92, 0x86, 0x00, 0xa2, 0xd3, - 0xff, 0x5b, 0x08, 0x7d, 0x2b, 0x65, 0xe4, 0xc3, 0x09, 0x68, 0x72, 0x21, 0xc4, 0xd8, 0x0a, 0x21, 0x9e, - 0x1f, 0xdf, 0xb2, 0xaa, 0x2b, 0x42, 0x68, 0xe7, 0xeb, 0x52, 0xf8, 0x9e, 0xfc, 0x7f, 0x0f, 0x55, 0x26, - 0x7d, 0x44, 0xfb, 0x35, 0xe5, 0xc2, 0x2c, 0xb6, 0x8d, 0x06, 0xc5, 0xdc, 0xbf, 0x66, 0xf6, 0xb2, 0xf2, - 0x9b, 0xe2, 0x49, 0xaf, 0xfd, 0x4c, 0x69, 0x46, 0x72, 0xe0, 0x2f, 0x31, 0x77, 0x86, 0x7b, 0x5b, 0x6d, - 0x49, 0xe6, 0xc7, 0x84, 0xd1, 0xdd, 0x56, 0x89, 0x8d, 0xbd, 0x07, 0x18, 0x01, 0x43, 0x70, 0x9b, 0x00, - 0x71, 0x16, 0x89, 0x66, 0x2e, 0xb6, 0x5f, 0x62, 0xeb, 0x96, 0xed, 0xf2, 0xdb, 0xdb, 0xcf, 0xdd, 0xa8, - 0xab, 0xde, 0x93, 0xb3, 0xdb, 0x54, 0xf0, 0x34, 0x4a, 0x28, 0xc3, 0x11, 0xf6, 0xb9, 0xd6, 0x45, 0x3f, - 0x07, 0xc0, 0x8e, 0x10, 0x7a, 0x2b, 0x56, 0x15, 0xbb, 0x00, 0x9d, 0x82, 0x27, 0xf2, 0x11, 0xa3, 0xda, - 0x03, 0xaa, 0x51, 0xc0, 0xfd, 0x90, 0xc8, 0x73, 0x81, 0xce, 0x97, 0x30, 0xa2, 0x54, 0x63, 0x6f, 0xfc, - 0x7f, 0x5b, 0x71, 0xec, 0x11, 0xb0, 0xa0, 0xc8, 0x74, 0x3a, 0xcc, 0x1b, 0x5e, 0xcd, 0x91, 0xa8, 0x18, - 0x92, 0xeb, 0x33, 0xc4, 0x6d, 0xb8, 0x16, 0x67, 0xe1, 0xc5, 0xa6, 0x26, 0x35, 0x48, 0xc4, 0xe7, 0x94, - 0xeb, 0xbb, 0xb8, 0xde, 0xd3, 0xe1, 0xc0, 0xcb, 0x00, 0x20, 0xf6, 0xbc, 0xa9, 0xc5, 0x70, 0xc4, 0xda, - 0x1b, 0x61, 0x0b, 0x9f, 0x0b, 0x19, 0x93, 0xaf, 0x8f, 0x40, 0xbb, 0x26, 0x79, 0x02, 0x03, 0x01, 0x00, - 0x01, 0xa3, 0x82, 0x01, 0x1d, 0x30, 0x82, 0x01, 0x19, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, - 0x16, 0x04, 0x14, 0xa3, 0xda, 0xe5, 0xef, 0xc3, 0x1c, 0x7a, 0xcf, 0x34, 0x2b, 0xa2, 0x42, 0x2b, 0x77, - 0xcb, 0x62, 0xfb, 0x4c, 0x28, 0x51, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, - 0x80, 0x14, 0x9c, 0xe1, 0xad, 0x8f, 0xd4, 0x86, 0xd2, 0x1c, 0x7e, 0x48, 0x32, 0xf2, 0x28, 0xfe, 0x87, - 0x90, 0xe3, 0xb1, 0xc5, 0x8e, 0x30, 0x4a, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x43, 0x30, 0x41, 0x30, - 0x3f, 0xa0, 0x3d, 0xa0, 0x3b, 0x86, 0x39, 0x66, 0x69, 0x6c, 0x65, 0x3a, 0x2f, 0x2f, 0x2f, 0x2f, 0x52, - 0x44, 0x32, 0x38, 0x31, 0x38, 0x37, 0x38, 0x30, 0x45, 0x33, 0x45, 0x45, 0x43, 0x2f, 0x43, 0x65, 0x72, - 0x74, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x2f, 0x50, 0x72, 0x6f, 0x64, 0x32, 0x4c, 0x53, 0x52, 0x41, - 0x73, 0x68, 0x61, 0x32, 0x52, 0x44, 0x53, 0x4c, 0x4d, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x64, 0x06, 0x08, - 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x58, 0x30, 0x56, 0x30, 0x54, 0x06, 0x08, 0x2b, - 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x48, 0x66, 0x69, 0x6c, 0x65, 0x3a, 0x2f, 0x2f, 0x2f, - 0x2f, 0x52, 0x44, 0x32, 0x38, 0x31, 0x38, 0x37, 0x38, 0x30, 0x45, 0x33, 0x45, 0x45, 0x43, 0x2f, 0x43, - 0x65, 0x72, 0x74, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x2f, 0x52, 0x44, 0x32, 0x38, 0x31, 0x38, 0x37, - 0x38, 0x30, 0x45, 0x33, 0x45, 0x45, 0x43, 0x5f, 0x50, 0x72, 0x6f, 0x64, 0x32, 0x4c, 0x53, 0x52, 0x41, - 0x73, 0x68, 0x61, 0x32, 0x52, 0x44, 0x53, 0x4c, 0x4d, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x0c, 0x06, 0x03, - 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x17, 0x06, 0x08, 0x2b, 0x06, 0x01, - 0x04, 0x01, 0x82, 0x37, 0x12, 0x04, 0x0b, 0x16, 0x09, 0x54, 0x4c, 0x53, 0x7e, 0x42, 0x41, 0x53, 0x49, - 0x43, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, - 0x82, 0x01, 0x01, 0x00, 0x55, 0xd5, 0x94, 0x3b, 0x06, 0xef, 0xf2, 0xb0, 0xf9, 0xd7, 0x36, 0x2a, 0x36, - 0xe0, 0xf1, 0xd9, 0x18, 0xc1, 0x89, 0x7e, 0xa2, 0xcf, 0x01, 0x6f, 0x22, 0x7b, 0x34, 0x81, 0xf0, 0x7a, - 0x45, 0x11, 0x6e, 0x75, 0x4b, 0x0b, 0xa8, 0xcd, 0x92, 0x57, 0x19, 0x80, 0xb7, 0x6e, 0x1a, 0x4d, 0x12, - 0x65, 0x91, 0x56, 0x38, 0x17, 0x22, 0xa2, 0x75, 0xae, 0xf9, 0x12, 0x75, 0x38, 0xf3, 0x19, 0x74, 0xea, - 0x87, 0x46, 0x1f, 0x98, 0x2c, 0x2f, 0xf9, 0xfc, 0xb4, 0xdc, 0x25, 0xa0, 0xd3, 0x34, 0x1b, 0xbc, 0x21, - 0xbb, 0x3d, 0x82, 0xad, 0x15, 0xc6, 0x3d, 0x02, 0x75, 0x33, 0x70, 0x25, 0x0a, 0x1a, 0xf7, 0x4c, 0xcb, - 0x84, 0xa3, 0xc1, 0x78, 0xe6, 0xf5, 0xa1, 0x44, 0x54, 0xc8, 0x34, 0xfd, 0xef, 0xbf, 0x86, 0x81, 0x9d, - 0x9a, 0x7e, 0xb6, 0xad, 0x71, 0x7e, 0xe4, 0xd9, 0x71, 0x6c, 0xb9, 0xe7, 0xf2, 0xd6, 0xd7, 0xbb, 0x66, - 0x5a, 0x30, 0xf5, 0x29, 0xae, 0x02, 0x39, 0x3d, 0xea, 0x7a, 0x79, 0x1b, 0x53, 0xc5, 0xbe, 0x8d, 0xfb, - 0xe2, 0xe4, 0x8e, 0xc2, 0x04, 0xb3, 0x0a, 0x94, 0x75, 0xa3, 0xbf, 0xd4, 0x87, 0xd2, 0x74, 0x15, 0x05, - 0x5e, 0xd5, 0x8f, 0x94, 0x23, 0x41, 0x13, 0x3f, 0xbd, 0xed, 0x21, 0x55, 0x96, 0xe9, 0xc4, 0x93, 0x34, - 0x7f, 0xaa, 0xea, 0xe7, 0xb1, 0x9a, 0xca, 0x25, 0x91, 0x18, 0xdf, 0x28, 0x05, 0x8e, 0x53, 0xb3, 0x8c, - 0x8d, 0xcc, 0xf3, 0xf4, 0x78, 0x76, 0x76, 0x7b, 0x82, 0xd6, 0x75, 0x7a, 0x7d, 0xb3, 0x23, 0x2c, 0xc7, - 0xbe, 0xa6, 0xb0, 0x50, 0x4d, 0x6c, 0xe2, 0x90, 0x85, 0x97, 0x77, 0x0d, 0x2f, 0xf5, 0x7b, 0xb0, 0xc6, - 0xad, 0xfa, 0x9a, 0x2c, 0xdf, 0xeb, 0x0d, 0x60, 0xd3, 0x0e, 0xa8, 0x5c, 0x43, 0xab, 0x09, 0x85, 0xa3, - 0xa9, 0x31, 0x66, 0xbd, 0xe4], - vec![0x30, 0x82, 0x04, 0x59, 0x30, 0x82, 0x03, 0x45, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x05, 0x01, - 0x00, 0x00, 0x00, 0x02, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1d, 0x05, 0x00, 0x30, 0x11, - 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x06, 0x42, 0x65, 0x63, 0x6b, 0x65, 0x72, - 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x30, 0x32, 0x36, 0x32, 0x33, 0x32, 0x36, 0x34, 0x35, 0x5a, - 0x17, 0x0d, 0x33, 0x38, 0x30, 0x31, 0x31, 0x39, 0x30, 0x33, 0x31, 0x34, 0x30, 0x37, 0x5a, 0x30, 0x81, - 0xa6, 0x31, 0x81, 0xa3, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x1e, 0x20, 0x00, 0x6e, 0x00, 0x63, - 0x00, 0x61, 0x00, 0x63, 0x00, 0x6e, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x70, 0x00, 0x5f, 0x00, 0x74, 0x00, - 0x63, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x32, 0x00, 0x37, 0x30, 0x33, 0x06, 0x03, 0x55, 0x04, - 0x07, 0x1e, 0x2c, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6e, 0x00, 0x5f, 0x00, 0x69, - 0x00, 0x70, 0x00, 0x5f, 0x00, 0x74, 0x00, 0x63, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x32, 0x00, - 0x37, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x31, 0x30, 0x43, 0x06, 0x03, - 0x55, 0x04, 0x05, 0x1e, 0x3c, 0x00, 0x31, 0x00, 0x42, 0x00, 0x63, 0x00, 0x4b, 0x00, 0x65, 0x00, 0x56, - 0x00, 0x33, 0x00, 0x4d, 0x00, 0x67, 0x00, 0x74, 0x00, 0x6a, 0x00, 0x55, 0x00, 0x74, 0x00, 0x6f, 0x00, - 0x32, 0x00, 0x50, 0x00, 0x49, 0x00, 0x68, 0x00, 0x35, 0x00, 0x52, 0x00, 0x57, 0x00, 0x56, 0x00, 0x36, - 0x00, 0x42, 0x00, 0x58, 0x00, 0x48, 0x00, 0x77, 0x00, 0x3d, 0x00, 0x0d, 0x00, 0x0a, 0x30, 0x58, 0x30, - 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x0f, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30, 0x48, 0x02, 0x41, - 0x00, 0xab, 0xac, 0x87, 0x11, 0x83, 0xbf, 0xe9, 0x48, 0x25, 0x00, 0x2c, 0x33, 0x31, 0x5e, 0x3d, 0x78, - 0xc8, 0x5f, 0x82, 0xcb, 0x36, 0x41, 0xf5, 0xb4, 0x65, 0x15, 0xee, 0x04, 0x31, 0xae, 0xe2, 0x48, 0x58, - 0x99, 0x7f, 0x4f, 0x90, 0x1d, 0xf7, 0x7c, 0xd7, 0xf8, 0x47, 0x93, 0xa0, 0xca, 0x9c, 0xdf, 0x91, 0xb0, - 0x41, 0xe8, 0x05, 0x4b, 0xdc, 0x24, 0x5b, 0x72, 0xf7, 0x68, 0x91, 0x84, 0xfb, 0x19, 0x02, 0x03, 0x01, - 0x00, 0x01, 0xa3, 0x82, 0x01, 0xf4, 0x30, 0x82, 0x01, 0xf0, 0x30, 0x14, 0x06, 0x09, 0x2b, 0x06, 0x01, - 0x04, 0x01, 0x82, 0x37, 0x12, 0x04, 0x01, 0x01, 0xff, 0x04, 0x04, 0x01, 0x00, 0x05, 0x00, 0x30, 0x3c, - 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x12, 0x02, 0x01, 0x01, 0xff, 0x04, 0x2c, 0x4d, - 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, - 0x20, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, - 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x30, 0x81, 0xdd, 0x06, 0x09, 0x2b, 0x06, 0x01, - 0x04, 0x01, 0x82, 0x37, 0x12, 0x05, 0x01, 0x01, 0xff, 0x04, 0x81, 0xcc, 0x00, 0x30, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x22, 0x04, 0x00, 0x00, 0x1c, 0x00, 0x4a, 0x00, 0x66, 0x00, - 0x4a, 0x00, 0xb0, 0x00, 0x03, 0x00, 0x33, 0x00, 0x64, 0x00, 0x32, 0x00, 0x36, 0x00, 0x37, 0x00, 0x39, - 0x00, 0x35, 0x00, 0x34, 0x00, 0x2d, 0x00, 0x65, 0x00, 0x65, 0x00, 0x62, 0x00, 0x37, 0x00, 0x2d, 0x00, - 0x31, 0x00, 0x31, 0x00, 0x64, 0x00, 0x31, 0x00, 0x2d, 0x00, 0x62, 0x00, 0x39, 0x00, 0x34, 0x00, 0x65, - 0x00, 0x2d, 0x00, 0x30, 0x00, 0x30, 0x00, 0x63, 0x00, 0x30, 0x00, 0x34, 0x00, 0x66, 0x00, 0x61, 0x00, - 0x33, 0x00, 0x30, 0x00, 0x38, 0x00, 0x30, 0x00, 0x64, 0x00, 0x00, 0x00, 0x33, 0x00, 0x64, 0x00, 0x32, - 0x00, 0x36, 0x00, 0x37, 0x00, 0x39, 0x00, 0x35, 0x00, 0x34, 0x00, 0x2d, 0x00, 0x65, 0x00, 0x65, 0x00, - 0x62, 0x00, 0x37, 0x00, 0x2d, 0x00, 0x31, 0x00, 0x31, 0x00, 0x64, 0x00, 0x31, 0x00, 0x2d, 0x00, 0x62, - 0x00, 0x39, 0x00, 0x34, 0x00, 0x65, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x30, 0x00, 0x63, 0x00, 0x30, 0x00, - 0x34, 0x00, 0x66, 0x00, 0x61, 0x00, 0x33, 0x00, 0x30, 0x00, 0x38, 0x00, 0x30, 0x00, 0x64, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x81, 0x80, 0x06, 0x09, - 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x12, 0x06, 0x01, 0x01, 0xff, 0x04, 0x70, 0x00, 0x30, 0x00, - 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x34, 0x00, - 0x4c, 0x00, 0x34, 0x00, 0x4c, 0x00, 0x36, 0x00, 0x41, 0x00, 0x4d, 0x00, 0x42, 0x00, 0x43, 0x00, 0x53, - 0x00, 0x51, 0x00, 0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x34, 0x00, 0x32, 0x00, 0x39, 0x00, 0x2d, 0x00, - 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x2d, 0x00, 0x33, 0x00, 0x34, 0x00, 0x39, - 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x41, 0x00, 0x54, 0x00, 0x33, 0x00, 0x35, 0x00, 0x33, 0x00, - 0x00, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x52, 0x00, 0x4b, 0x00, 0x47, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x55, - 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x37, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x01, 0x01, 0xff, - 0x04, 0x2d, 0x30, 0x2b, 0xa1, 0x22, 0xa4, 0x20, 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x34, - 0x00, 0x4c, 0x00, 0x34, 0x00, 0x4c, 0x00, 0x36, 0x00, 0x41, 0x00, 0x4d, 0x00, 0x42, 0x00, 0x43, 0x00, - 0x53, 0x00, 0x51, 0x00, 0x00, 0x00, 0x82, 0x05, 0x01, 0x00, 0x00, 0x00, 0x02, 0x30, 0x09, 0x06, 0x05, - 0x2b, 0x0e, 0x03, 0x02, 0x1d, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3e, 0xd3, 0xd5, 0x61, 0x8a, - 0x87, 0x7b, 0x98, 0x2c, 0x6d, 0x20, 0x38, 0x12, 0x08, 0xd8, 0xf7, 0x83, 0x08, 0xf8, 0xe6, 0xb2, 0xe1, - 0x21, 0xe1, 0x30, 0x61, 0x12, 0x19, 0xe8, 0xc1, 0x41, 0xaf, 0x59, 0x7c, 0x1e, 0x3e, 0xc8, 0x40, 0x9e, - 0x24, 0xe8, 0x8d, 0x0c, 0x41, 0xfd, 0xf8, 0x3e, 0xa1, 0xb3, 0xac, 0x56, 0xac, 0x52, 0x91, 0x5a, 0xf8, - 0xd0, 0x40, 0x8e, 0x13, 0x47, 0xa9, 0x8a, 0x0a, 0x62, 0x6d, 0x11, 0x89, 0x20, 0x56, 0xe7, 0xd6, 0x5f, - 0x12, 0x44, 0x94, 0xbf, 0x63, 0x99, 0xa3, 0x42, 0x40, 0xd5, 0xc6, 0x8c, 0x1f, 0x4b, 0xf8, 0xaf, 0x83, - 0x8e, 0xf6, 0x74, 0xb2, 0x0b, 0x55, 0x13, 0x4a, 0x76, 0xed, 0x37, 0xd8, 0x3d, 0x13, 0xe7, 0xae, 0x43, - 0x4c, 0x9a, 0x61, 0x6c, 0x7b, 0x1b, 0xd1, 0xaa, 0x00, 0x97, 0xdf, 0x5b, 0x85, 0x9f, 0xc8, 0xee, 0x6c, - 0xe5, 0xa2, 0x63, 0x76, 0xe4, 0x06, 0xd3, 0x2a, 0xe0, 0x55, 0xe1, 0x92, 0x78, 0xed, 0x03, 0x7b, 0x7d, - 0x1a, 0x6e, 0xc2, 0x56, 0xdc, 0xad, 0x6e, 0xd7, 0xa9, 0xfe, 0xa7, 0xfd, 0x09, 0x0a, 0xa6, 0xd5, 0x8a, - 0x99, 0xa4, 0x75, 0x89, 0xad, 0x84, 0xc7, 0x09, 0xf7, 0x4c, 0x6e, 0xd0, 0xe2, 0x80, 0x17, 0x62, 0xfa, - 0x86, 0xfe, 0x43, 0x51, 0xf2, 0xb4, 0xf6, 0xef, 0x3b, 0xb3, 0x3d, 0x1f, 0xef, 0xa3, 0xcb, 0xa2, 0x57, - 0x25, 0x7c, 0x02, 0xf2, 0x27, 0x1c, 0x87, 0x70, 0x8e, 0x84, 0x20, 0xfe, 0x1d, 0x4a, 0xc4, 0x87, 0x24, - 0x3b, 0xba, 0xff, 0x34, 0x1a, 0xe2, 0xff, 0xa2, 0x43, 0x39, 0xd8, 0x19, 0x97, 0xf8, 0xf0, 0xf9, 0x73, - 0xa6, 0xb6, 0x55, 0x64, 0xa6, 0xca, 0xa3, 0x48, 0x22, 0xb7, 0x1a, 0x9b, 0x98, 0x1a, 0x8e, 0x2f, 0xaa, - 0xec, 0xc1, 0xfe, 0x25, 0x36, 0x2b, 0x70, 0x97, 0x8c, 0x5b, 0x62, 0x21, 0xc3], + preamble_message_type: PreambleType::LicenseRequest, + preamble_flags: PreambleFlags::empty(), + preamble_version: PreambleVersion::V3, + preamble_message_size: 0, + }, + server_random: Vec::from(SERVER_RANDOM_BUFFER.as_ref()), + product_info: ProductInfo { + version: 0x60000, + company_name: "Microsoft Corporation".to_owned(), + product_id: "A02".to_owned(), + }, + server_certificate: Some(ServerCertificate { + issued_permanently: true, + certificate: CertificateType::X509(X509CertificateChain { + certificate_array: vec![ + vec![ + 0x30, 0x82, 0x03, 0xda, 0x30, 0x82, 0x02, 0xc2, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x13, 0x7f, + 0x00, 0x00, 0x01, 0x76, 0x00, 0x8f, 0x08, 0x64, 0x08, 0x68, 0xa7, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x76, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, + 0x00, 0x30, 0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x50, 0x72, + 0x6f, 0x64, 0x32, 0x4c, 0x53, 0x52, 0x41, 0x73, 0x68, 0x61, 0x32, 0x52, 0x44, 0x53, 0x4c, 0x4d, + 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x30, 0x32, 0x36, 0x32, 0x32, 0x35, 0x33, 0x34, 0x30, + 0x5a, 0x17, 0x0d, 0x32, 0x37, 0x30, 0x36, 0x30, 0x36, 0x32, 0x30, 0x34, 0x32, 0x33, 0x38, 0x5a, + 0x30, 0x11, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x06, 0x42, 0x65, 0x63, + 0x6b, 0x65, 0x72, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xa8, 0x6b, 0xda, 0xae, 0x08, 0x1d, 0xc5, 0x05, 0x70, 0x7d, 0xa0, 0x41, + 0x46, 0xb4, 0x14, 0xcf, 0xfb, 0x8e, 0x09, 0x0b, 0x0a, 0x52, 0x8a, 0x7f, 0x7a, 0x35, 0xb6, 0xe3, + 0x0d, 0x1c, 0xbe, 0x49, 0x63, 0x41, 0x92, 0x86, 0x00, 0xa2, 0xd3, 0xff, 0x5b, 0x08, 0x7d, 0x2b, + 0x65, 0xe4, 0xc3, 0x09, 0x68, 0x72, 0x21, 0xc4, 0xd8, 0x0a, 0x21, 0x9e, 0x1f, 0xdf, 0xb2, 0xaa, + 0x2b, 0x42, 0x68, 0xe7, 0xeb, 0x52, 0xf8, 0x9e, 0xfc, 0x7f, 0x0f, 0x55, 0x26, 0x7d, 0x44, 0xfb, + 0x35, 0xe5, 0xc2, 0x2c, 0xb6, 0x8d, 0x06, 0xc5, 0xdc, 0xbf, 0x66, 0xf6, 0xb2, 0xf2, 0x9b, 0xe2, + 0x49, 0xaf, 0xfd, 0x4c, 0x69, 0x46, 0x72, 0xe0, 0x2f, 0x31, 0x77, 0x86, 0x7b, 0x5b, 0x6d, 0x49, + 0xe6, 0xc7, 0x84, 0xd1, 0xdd, 0x56, 0x89, 0x8d, 0xbd, 0x07, 0x18, 0x01, 0x43, 0x70, 0x9b, 0x00, + 0x71, 0x16, 0x89, 0x66, 0x2e, 0xb6, 0x5f, 0x62, 0xeb, 0x96, 0xed, 0xf2, 0xdb, 0xdb, 0xcf, 0xdd, + 0xa8, 0xab, 0xde, 0x93, 0xb3, 0xdb, 0x54, 0xf0, 0x34, 0x4a, 0x28, 0xc3, 0x11, 0xf6, 0xb9, 0xd6, + 0x45, 0x3f, 0x07, 0xc0, 0x8e, 0x10, 0x7a, 0x2b, 0x56, 0x15, 0xbb, 0x00, 0x9d, 0x82, 0x27, 0xf2, + 0x11, 0xa3, 0xda, 0x03, 0xaa, 0x51, 0xc0, 0xfd, 0x90, 0xc8, 0x73, 0x81, 0xce, 0x97, 0x30, 0xa2, + 0x54, 0x63, 0x6f, 0xfc, 0x7f, 0x5b, 0x71, 0xec, 0x11, 0xb0, 0xa0, 0xc8, 0x74, 0x3a, 0xcc, 0x1b, + 0x5e, 0xcd, 0x91, 0xa8, 0x18, 0x92, 0xeb, 0x33, 0xc4, 0x6d, 0xb8, 0x16, 0x67, 0xe1, 0xc5, 0xa6, + 0x26, 0x35, 0x48, 0xc4, 0xe7, 0x94, 0xeb, 0xbb, 0xb8, 0xde, 0xd3, 0xe1, 0xc0, 0xcb, 0x00, 0x20, + 0xf6, 0xbc, 0xa9, 0xc5, 0x70, 0xc4, 0xda, 0x1b, 0x61, 0x0b, 0x9f, 0x0b, 0x19, 0x93, 0xaf, 0x8f, + 0x40, 0xbb, 0x26, 0x79, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x1d, 0x30, 0x82, 0x01, + 0x19, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xa3, 0xda, 0xe5, 0xef, + 0xc3, 0x1c, 0x7a, 0xcf, 0x34, 0x2b, 0xa2, 0x42, 0x2b, 0x77, 0xcb, 0x62, 0xfb, 0x4c, 0x28, 0x51, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x9c, 0xe1, 0xad, + 0x8f, 0xd4, 0x86, 0xd2, 0x1c, 0x7e, 0x48, 0x32, 0xf2, 0x28, 0xfe, 0x87, 0x90, 0xe3, 0xb1, 0xc5, + 0x8e, 0x30, 0x4a, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x43, 0x30, 0x41, 0x30, 0x3f, 0xa0, 0x3d, + 0xa0, 0x3b, 0x86, 0x39, 0x66, 0x69, 0x6c, 0x65, 0x3a, 0x2f, 0x2f, 0x2f, 0x2f, 0x52, 0x44, 0x32, + 0x38, 0x31, 0x38, 0x37, 0x38, 0x30, 0x45, 0x33, 0x45, 0x45, 0x43, 0x2f, 0x43, 0x65, 0x72, 0x74, + 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x2f, 0x50, 0x72, 0x6f, 0x64, 0x32, 0x4c, 0x53, 0x52, 0x41, + 0x73, 0x68, 0x61, 0x32, 0x52, 0x44, 0x53, 0x4c, 0x4d, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x64, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x58, 0x30, 0x56, 0x30, 0x54, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x48, 0x66, 0x69, 0x6c, 0x65, 0x3a, + 0x2f, 0x2f, 0x2f, 0x2f, 0x52, 0x44, 0x32, 0x38, 0x31, 0x38, 0x37, 0x38, 0x30, 0x45, 0x33, 0x45, + 0x45, 0x43, 0x2f, 0x43, 0x65, 0x72, 0x74, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x2f, 0x52, 0x44, + 0x32, 0x38, 0x31, 0x38, 0x37, 0x38, 0x30, 0x45, 0x33, 0x45, 0x45, 0x43, 0x5f, 0x50, 0x72, 0x6f, + 0x64, 0x32, 0x4c, 0x53, 0x52, 0x41, 0x73, 0x68, 0x61, 0x32, 0x52, 0x44, 0x53, 0x4c, 0x4d, 0x2e, + 0x63, 0x72, 0x74, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, + 0x00, 0x30, 0x17, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x12, 0x04, 0x0b, 0x16, + 0x09, 0x54, 0x4c, 0x53, 0x7e, 0x42, 0x41, 0x53, 0x49, 0x43, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x55, 0xd5, + 0x94, 0x3b, 0x06, 0xef, 0xf2, 0xb0, 0xf9, 0xd7, 0x36, 0x2a, 0x36, 0xe0, 0xf1, 0xd9, 0x18, 0xc1, + 0x89, 0x7e, 0xa2, 0xcf, 0x01, 0x6f, 0x22, 0x7b, 0x34, 0x81, 0xf0, 0x7a, 0x45, 0x11, 0x6e, 0x75, + 0x4b, 0x0b, 0xa8, 0xcd, 0x92, 0x57, 0x19, 0x80, 0xb7, 0x6e, 0x1a, 0x4d, 0x12, 0x65, 0x91, 0x56, + 0x38, 0x17, 0x22, 0xa2, 0x75, 0xae, 0xf9, 0x12, 0x75, 0x38, 0xf3, 0x19, 0x74, 0xea, 0x87, 0x46, + 0x1f, 0x98, 0x2c, 0x2f, 0xf9, 0xfc, 0xb4, 0xdc, 0x25, 0xa0, 0xd3, 0x34, 0x1b, 0xbc, 0x21, 0xbb, + 0x3d, 0x82, 0xad, 0x15, 0xc6, 0x3d, 0x02, 0x75, 0x33, 0x70, 0x25, 0x0a, 0x1a, 0xf7, 0x4c, 0xcb, + 0x84, 0xa3, 0xc1, 0x78, 0xe6, 0xf5, 0xa1, 0x44, 0x54, 0xc8, 0x34, 0xfd, 0xef, 0xbf, 0x86, 0x81, + 0x9d, 0x9a, 0x7e, 0xb6, 0xad, 0x71, 0x7e, 0xe4, 0xd9, 0x71, 0x6c, 0xb9, 0xe7, 0xf2, 0xd6, 0xd7, + 0xbb, 0x66, 0x5a, 0x30, 0xf5, 0x29, 0xae, 0x02, 0x39, 0x3d, 0xea, 0x7a, 0x79, 0x1b, 0x53, 0xc5, + 0xbe, 0x8d, 0xfb, 0xe2, 0xe4, 0x8e, 0xc2, 0x04, 0xb3, 0x0a, 0x94, 0x75, 0xa3, 0xbf, 0xd4, 0x87, + 0xd2, 0x74, 0x15, 0x05, 0x5e, 0xd5, 0x8f, 0x94, 0x23, 0x41, 0x13, 0x3f, 0xbd, 0xed, 0x21, 0x55, + 0x96, 0xe9, 0xc4, 0x93, 0x34, 0x7f, 0xaa, 0xea, 0xe7, 0xb1, 0x9a, 0xca, 0x25, 0x91, 0x18, 0xdf, + 0x28, 0x05, 0x8e, 0x53, 0xb3, 0x8c, 0x8d, 0xcc, 0xf3, 0xf4, 0x78, 0x76, 0x76, 0x7b, 0x82, 0xd6, + 0x75, 0x7a, 0x7d, 0xb3, 0x23, 0x2c, 0xc7, 0xbe, 0xa6, 0xb0, 0x50, 0x4d, 0x6c, 0xe2, 0x90, 0x85, + 0x97, 0x77, 0x0d, 0x2f, 0xf5, 0x7b, 0xb0, 0xc6, 0xad, 0xfa, 0x9a, 0x2c, 0xdf, 0xeb, 0x0d, 0x60, + 0xd3, 0x0e, 0xa8, 0x5c, 0x43, 0xab, 0x09, 0x85, 0xa3, 0xa9, 0x31, 0x66, 0xbd, 0xe4, ], - }), + vec![ + 0x30, 0x82, 0x04, 0x59, 0x30, 0x82, 0x03, 0x45, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x05, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1d, 0x05, 0x00, 0x30, + 0x11, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x06, 0x42, 0x65, 0x63, 0x6b, + 0x65, 0x72, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x30, 0x32, 0x36, 0x32, 0x33, 0x32, 0x36, + 0x34, 0x35, 0x5a, 0x17, 0x0d, 0x33, 0x38, 0x30, 0x31, 0x31, 0x39, 0x30, 0x33, 0x31, 0x34, 0x30, + 0x37, 0x5a, 0x30, 0x81, 0xa6, 0x31, 0x81, 0xa3, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x1e, + 0x20, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6e, 0x00, 0x5f, 0x00, 0x69, 0x00, + 0x70, 0x00, 0x5f, 0x00, 0x74, 0x00, 0x63, 0x00, 0x70, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x32, 0x00, + 0x37, 0x30, 0x33, 0x06, 0x03, 0x55, 0x04, 0x07, 0x1e, 0x2c, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x61, + 0x00, 0x63, 0x00, 0x6e, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x70, 0x00, 0x5f, 0x00, 0x74, 0x00, 0x63, + 0x00, 0x70, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x32, 0x00, 0x37, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x2e, + 0x00, 0x30, 0x00, 0x2e, 0x00, 0x31, 0x30, 0x43, 0x06, 0x03, 0x55, 0x04, 0x05, 0x1e, 0x3c, 0x00, + 0x31, 0x00, 0x42, 0x00, 0x63, 0x00, 0x4b, 0x00, 0x65, 0x00, 0x56, 0x00, 0x33, 0x00, 0x4d, 0x00, + 0x67, 0x00, 0x74, 0x00, 0x6a, 0x00, 0x55, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x32, 0x00, 0x50, 0x00, + 0x49, 0x00, 0x68, 0x00, 0x35, 0x00, 0x52, 0x00, 0x57, 0x00, 0x56, 0x00, 0x36, 0x00, 0x42, 0x00, + 0x58, 0x00, 0x48, 0x00, 0x77, 0x00, 0x3d, 0x00, 0x0d, 0x00, 0x0a, 0x30, 0x58, 0x30, 0x09, 0x06, + 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x0f, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30, 0x48, 0x02, 0x41, 0x00, + 0xab, 0xac, 0x87, 0x11, 0x83, 0xbf, 0xe9, 0x48, 0x25, 0x00, 0x2c, 0x33, 0x31, 0x5e, 0x3d, 0x78, + 0xc8, 0x5f, 0x82, 0xcb, 0x36, 0x41, 0xf5, 0xb4, 0x65, 0x15, 0xee, 0x04, 0x31, 0xae, 0xe2, 0x48, + 0x58, 0x99, 0x7f, 0x4f, 0x90, 0x1d, 0xf7, 0x7c, 0xd7, 0xf8, 0x47, 0x93, 0xa0, 0xca, 0x9c, 0xdf, + 0x91, 0xb0, 0x41, 0xe8, 0x05, 0x4b, 0xdc, 0x24, 0x5b, 0x72, 0xf7, 0x68, 0x91, 0x84, 0xfb, 0x19, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xf4, 0x30, 0x82, 0x01, 0xf0, 0x30, 0x14, 0x06, + 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x12, 0x04, 0x01, 0x01, 0xff, 0x04, 0x04, 0x01, + 0x00, 0x05, 0x00, 0x30, 0x3c, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x12, 0x02, + 0x01, 0x01, 0xff, 0x04, 0x2c, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, + 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x20, 0x00, 0x43, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x70, + 0x00, 0x6f, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x00, + 0x00, 0x30, 0x81, 0xdd, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x12, 0x05, 0x01, + 0x01, 0xff, 0x04, 0x81, 0xcc, 0x00, 0x30, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x22, 0x04, 0x00, 0x00, 0x1c, 0x00, 0x4a, 0x00, 0x66, 0x00, 0x4a, 0x00, 0xb0, 0x00, 0x03, + 0x00, 0x33, 0x00, 0x64, 0x00, 0x32, 0x00, 0x36, 0x00, 0x37, 0x00, 0x39, 0x00, 0x35, 0x00, 0x34, + 0x00, 0x2d, 0x00, 0x65, 0x00, 0x65, 0x00, 0x62, 0x00, 0x37, 0x00, 0x2d, 0x00, 0x31, 0x00, 0x31, + 0x00, 0x64, 0x00, 0x31, 0x00, 0x2d, 0x00, 0x62, 0x00, 0x39, 0x00, 0x34, 0x00, 0x65, 0x00, 0x2d, + 0x00, 0x30, 0x00, 0x30, 0x00, 0x63, 0x00, 0x30, 0x00, 0x34, 0x00, 0x66, 0x00, 0x61, 0x00, 0x33, + 0x00, 0x30, 0x00, 0x38, 0x00, 0x30, 0x00, 0x64, 0x00, 0x00, 0x00, 0x33, 0x00, 0x64, 0x00, 0x32, + 0x00, 0x36, 0x00, 0x37, 0x00, 0x39, 0x00, 0x35, 0x00, 0x34, 0x00, 0x2d, 0x00, 0x65, 0x00, 0x65, + 0x00, 0x62, 0x00, 0x37, 0x00, 0x2d, 0x00, 0x31, 0x00, 0x31, 0x00, 0x64, 0x00, 0x31, 0x00, 0x2d, + 0x00, 0x62, 0x00, 0x39, 0x00, 0x34, 0x00, 0x65, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x30, 0x00, 0x63, + 0x00, 0x30, 0x00, 0x34, 0x00, 0x66, 0x00, 0x61, 0x00, 0x33, 0x00, 0x30, 0x00, 0x38, 0x00, 0x30, + 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x81, 0x80, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x12, 0x06, 0x01, + 0x01, 0xff, 0x04, 0x70, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x57, 0x00, + 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x34, 0x00, 0x4c, 0x00, 0x34, 0x00, 0x4c, 0x00, 0x36, 0x00, + 0x41, 0x00, 0x4d, 0x00, 0x42, 0x00, 0x43, 0x00, 0x53, 0x00, 0x51, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x34, 0x00, 0x32, 0x00, 0x39, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x2d, 0x00, 0x33, 0x00, 0x34, 0x00, 0x39, 0x00, 0x37, 0x00, 0x32, 0x00, + 0x2d, 0x00, 0x41, 0x00, 0x54, 0x00, 0x33, 0x00, 0x35, 0x00, 0x33, 0x00, 0x00, 0x00, 0x57, 0x00, + 0x4f, 0x00, 0x52, 0x00, 0x4b, 0x00, 0x47, 0x00, 0x52, 0x00, 0x4f, 0x00, 0x55, 0x00, 0x50, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x37, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x01, 0x01, 0xff, 0x04, 0x2d, + 0x30, 0x2b, 0xa1, 0x22, 0xa4, 0x20, 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x34, 0x00, + 0x4c, 0x00, 0x34, 0x00, 0x4c, 0x00, 0x36, 0x00, 0x41, 0x00, 0x4d, 0x00, 0x42, 0x00, 0x43, 0x00, + 0x53, 0x00, 0x51, 0x00, 0x00, 0x00, 0x82, 0x05, 0x01, 0x00, 0x00, 0x00, 0x02, 0x30, 0x09, 0x06, + 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1d, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3e, 0xd3, 0xd5, + 0x61, 0x8a, 0x87, 0x7b, 0x98, 0x2c, 0x6d, 0x20, 0x38, 0x12, 0x08, 0xd8, 0xf7, 0x83, 0x08, 0xf8, + 0xe6, 0xb2, 0xe1, 0x21, 0xe1, 0x30, 0x61, 0x12, 0x19, 0xe8, 0xc1, 0x41, 0xaf, 0x59, 0x7c, 0x1e, + 0x3e, 0xc8, 0x40, 0x9e, 0x24, 0xe8, 0x8d, 0x0c, 0x41, 0xfd, 0xf8, 0x3e, 0xa1, 0xb3, 0xac, 0x56, + 0xac, 0x52, 0x91, 0x5a, 0xf8, 0xd0, 0x40, 0x8e, 0x13, 0x47, 0xa9, 0x8a, 0x0a, 0x62, 0x6d, 0x11, + 0x89, 0x20, 0x56, 0xe7, 0xd6, 0x5f, 0x12, 0x44, 0x94, 0xbf, 0x63, 0x99, 0xa3, 0x42, 0x40, 0xd5, + 0xc6, 0x8c, 0x1f, 0x4b, 0xf8, 0xaf, 0x83, 0x8e, 0xf6, 0x74, 0xb2, 0x0b, 0x55, 0x13, 0x4a, 0x76, + 0xed, 0x37, 0xd8, 0x3d, 0x13, 0xe7, 0xae, 0x43, 0x4c, 0x9a, 0x61, 0x6c, 0x7b, 0x1b, 0xd1, 0xaa, + 0x00, 0x97, 0xdf, 0x5b, 0x85, 0x9f, 0xc8, 0xee, 0x6c, 0xe5, 0xa2, 0x63, 0x76, 0xe4, 0x06, 0xd3, + 0x2a, 0xe0, 0x55, 0xe1, 0x92, 0x78, 0xed, 0x03, 0x7b, 0x7d, 0x1a, 0x6e, 0xc2, 0x56, 0xdc, 0xad, + 0x6e, 0xd7, 0xa9, 0xfe, 0xa7, 0xfd, 0x09, 0x0a, 0xa6, 0xd5, 0x8a, 0x99, 0xa4, 0x75, 0x89, 0xad, + 0x84, 0xc7, 0x09, 0xf7, 0x4c, 0x6e, 0xd0, 0xe2, 0x80, 0x17, 0x62, 0xfa, 0x86, 0xfe, 0x43, 0x51, + 0xf2, 0xb4, 0xf6, 0xef, 0x3b, 0xb3, 0x3d, 0x1f, 0xef, 0xa3, 0xcb, 0xa2, 0x57, 0x25, 0x7c, 0x02, + 0xf2, 0x27, 0x1c, 0x87, 0x70, 0x8e, 0x84, 0x20, 0xfe, 0x1d, 0x4a, 0xc4, 0x87, 0x24, 0x3b, 0xba, + 0xff, 0x34, 0x1a, 0xe2, 0xff, 0xa2, 0x43, 0x39, 0xd8, 0x19, 0x97, 0xf8, 0xf0, 0xf9, 0x73, 0xa6, + 0xb6, 0x55, 0x64, 0xa6, 0xca, 0xa3, 0x48, 0x22, 0xb7, 0x1a, 0x9b, 0x98, 0x1a, 0x8e, 0x2f, 0xaa, + 0xec, 0xc1, 0xfe, 0x25, 0x36, 0x2b, 0x70, 0x97, 0x8c, 0x5b, 0x62, 0x21, 0xc3, + ], + ], }), - scope_list: vec![Scope(String::from("microsoft.com"))], - }; - req.license_header.preamble_message_size = u16::try_from(req.size()).expect("can't panic"); - req.into() + }), + scope_list: vec![Scope(String::from("microsoft.com"))], }; -} + req.license_header.preamble_message_size = u16::try_from(req.size()).expect("can't panic"); + req.into() +}); #[test] fn from_buffer_correctly_parses_client_new_license_request() { diff --git a/crates/ironrdp-pdu/src/rdp/server_license/client_platform_challenge_response/test.rs b/crates/ironrdp-pdu/src/rdp/server_license/client_platform_challenge_response/test.rs index 3ade8770..d086f954 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/client_platform_challenge_response/test.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/client_platform_challenge_response/test.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::*; use crate::rdp::server_license::{LicensePdu, BASIC_SECURITY_HEADER_SIZE}; @@ -44,37 +45,37 @@ const DATA_BUFFER: [u8; 16] = [ 0xf1, 0x59, 0x87, 0x3e, 0xc9, 0xd8, 0x98, 0xaf, 0x24, 0x02, 0xf8, 0xf3, 0x29, 0x3a, 0xf0, 0x26, ]; -lazy_static! { - pub(crate) static ref RESPONSE: PlatformChallengeResponseData = PlatformChallengeResponseData { - client_type: ClientType::Win32, - license_detail_level: LicenseDetailLevel::Detail, - challenge: Vec::from(CHALLENGE_BUFFER.as_ref()), - }; - pub(crate) static ref CLIENT_HARDWARE_IDENTIFICATION: ClientHardwareIdentification = ClientHardwareIdentification { +pub(crate) static RESPONSE: LazyLock = LazyLock::new(|| PlatformChallengeResponseData { + client_type: ClientType::Win32, + license_detail_level: LicenseDetailLevel::Detail, + challenge: Vec::from(CHALLENGE_BUFFER.as_ref()), +}); +pub(crate) static CLIENT_HARDWARE_IDENTIFICATION: LazyLock = + LazyLock::new(|| ClientHardwareIdentification { platform_id: HARDWARE_ID, data: Vec::from(DATA_BUFFER.as_ref()), - }; - pub(crate) static ref CLIENT_PLATFORM_CHALLENGE_RESPONSE: LicensePdu = - LicensePdu::ClientPlatformChallengeResponse(ClientPlatformChallengeResponse { - license_header: LicenseHeader { - security_header: BasicSecurityHeader { - flags: BasicSecurityHeaderFlags::LICENSE_PKT, - }, - preamble_message_type: PreambleType::PlatformChallengeResponse, - preamble_flags: PreambleFlags::empty(), - preamble_version: PreambleVersion::V3, - preamble_message_size: u16::try_from( - CLIENT_PLATFORM_CHALLENGE_RESPONSE_BUFFER.len() - BASIC_SECURITY_HEADER_SIZE - ) - .expect("can't panic"), + }); +pub(crate) static CLIENT_PLATFORM_CHALLENGE_RESPONSE: LazyLock = LazyLock::new(|| { + LicensePdu::ClientPlatformChallengeResponse(ClientPlatformChallengeResponse { + license_header: LicenseHeader { + security_header: BasicSecurityHeader { + flags: BasicSecurityHeaderFlags::LICENSE_PKT, }, - encrypted_challenge_response_data: Vec::from(&CLIENT_PLATFORM_CHALLENGE_RESPONSE_BUFFER[12..30]), - encrypted_hwid: Vec::from(&CLIENT_PLATFORM_CHALLENGE_RESPONSE_BUFFER[34..54]), - mac_data: Vec::from( - &CLIENT_PLATFORM_CHALLENGE_RESPONSE_BUFFER[CLIENT_PLATFORM_CHALLENGE_RESPONSE_BUFFER.len() - 16..] - ), - }); -} + preamble_message_type: PreambleType::PlatformChallengeResponse, + preamble_flags: PreambleFlags::empty(), + preamble_version: PreambleVersion::V3, + preamble_message_size: u16::try_from( + CLIENT_PLATFORM_CHALLENGE_RESPONSE_BUFFER.len() - BASIC_SECURITY_HEADER_SIZE, + ) + .expect("can't panic"), + }, + encrypted_challenge_response_data: Vec::from(&CLIENT_PLATFORM_CHALLENGE_RESPONSE_BUFFER[12..30]), + encrypted_hwid: Vec::from(&CLIENT_PLATFORM_CHALLENGE_RESPONSE_BUFFER[34..54]), + mac_data: Vec::from( + &CLIENT_PLATFORM_CHALLENGE_RESPONSE_BUFFER[CLIENT_PLATFORM_CHALLENGE_RESPONSE_BUFFER.len() - 16..], + ), + }) +}); #[test] fn from_buffer_correctly_parses_platform_challenge_response_data() { diff --git a/crates/ironrdp-pdu/src/rdp/server_license/licensing_error_message/test.rs b/crates/ironrdp-pdu/src/rdp/server_license/licensing_error_message/test.rs index 5ef7cb03..7ccfc467 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/licensing_error_message/test.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/licensing_error_message/test.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::*; use crate::rdp::server_license::LicensePdu; @@ -10,26 +11,24 @@ const LICENSE_MESSAGE_BUFFER: [u8; 12] = [ 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, // message ]; -lazy_static! { - pub static ref LICENSING_ERROR_MESSAGE: LicensePdu = { - let mut pdu = LicensingErrorMessage { - license_header: LicenseHeader { - security_header: BasicSecurityHeader { - flags: BasicSecurityHeaderFlags::LICENSE_PKT, - }, - preamble_message_type: PreambleType::ErrorAlert, - preamble_flags: PreambleFlags::empty(), - preamble_version: PreambleVersion::V3, - preamble_message_size: 0, +static LICENSING_ERROR_MESSAGE: LazyLock = LazyLock::new(|| { + let mut pdu = LicensingErrorMessage { + license_header: LicenseHeader { + security_header: BasicSecurityHeader { + flags: BasicSecurityHeaderFlags::LICENSE_PKT, }, - error_code: LicenseErrorCode::StatusValidClient, - state_transition: LicensingStateTransition::NoTransition, - error_info: Vec::new(), - }; - pdu.license_header.preamble_message_size = u16::try_from(pdu.size()).expect("can't panic"); - pdu.into() + preamble_message_type: PreambleType::ErrorAlert, + preamble_flags: PreambleFlags::empty(), + preamble_version: PreambleVersion::V3, + preamble_message_size: 0, + }, + error_code: LicenseErrorCode::StatusValidClient, + state_transition: LicensingStateTransition::NoTransition, + error_info: Vec::new(), }; -} + pdu.license_header.preamble_message_size = u16::try_from(pdu.size()).expect("can't panic"); + pdu.into() +}); #[test] fn from_buffer_correctly_parses_licensing_error_message() { diff --git a/crates/ironrdp-pdu/src/rdp/server_license/server_license_request/tests.rs b/crates/ironrdp-pdu/src/rdp/server_license/server_license_request/tests.rs index d6dc7314..37bbd951 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/server_license_request/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/server_license_request/tests.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::cert::{RsaPublicKey, PROP_CERT_BLOBS_HEADERS_SIZE, PROP_CERT_NO_BLOBS_SIZE, RSA_KEY_SIZE_WITHOUT_MODULUS}; use super::*; @@ -210,62 +211,60 @@ const SCOPE_BUFFER: [u8; 18] = [ 0x00, // scope array ]; -lazy_static! { - pub static ref PROPRIETARY_CERTIFICATE: ProprietaryCertificate = ProprietaryCertificate { - public_key: RsaPublicKey { - public_exponent: 0x0001_0001, - modulus: Vec::from(MODULUS.as_ref()), - }, - signature: Vec::from(SIGNATURE.as_ref()), - }; - pub static ref PRODUCT_INFO: ProductInfo = ProductInfo { - version: 0x60000, - company_name: "Microsoft Corporation".to_owned(), - product_id: "A02".to_owned(), - }; - pub static ref PUBLIC_KEY: RsaPublicKey = RsaPublicKey { +static PROPRIETARY_CERTIFICATE: LazyLock = LazyLock::new(|| ProprietaryCertificate { + public_key: RsaPublicKey { public_exponent: 0x0001_0001, modulus: Vec::from(MODULUS.as_ref()), - }; - pub static ref SERVER_LICENSE_REQUEST: LicensePdu = { - let mut req = ServerLicenseRequest { - license_header: LicenseHeader { - security_header: BasicSecurityHeader { - flags: BasicSecurityHeaderFlags::LICENSE_PKT, - }, - preamble_message_type: PreambleType::LicenseRequest, - preamble_flags: PreambleFlags::empty(), - preamble_version: PreambleVersion::V3, - preamble_message_size: 0, + }, + signature: Vec::from(SIGNATURE.as_ref()), +}); +static PRODUCT_INFO: LazyLock = LazyLock::new(|| ProductInfo { + version: 0x60000, + company_name: "Microsoft Corporation".to_owned(), + product_id: "A02".to_owned(), +}); +static PUBLIC_KEY: LazyLock = LazyLock::new(|| RsaPublicKey { + public_exponent: 0x0001_0001, + modulus: Vec::from(MODULUS.as_ref()), +}); +static SERVER_LICENSE_REQUEST: LazyLock = LazyLock::new(|| { + let mut req = ServerLicenseRequest { + license_header: LicenseHeader { + security_header: BasicSecurityHeader { + flags: BasicSecurityHeaderFlags::LICENSE_PKT, }, - server_random: Vec::from(SERVER_RANDOM_BUFFER.as_ref()), - product_info: ProductInfo { - version: 0x60000, - company_name: "Microsoft Corporation".to_owned(), - product_id: "A02".to_owned(), - }, - server_certificate: Some(ServerCertificate { - issued_permanently: true, - certificate: CertificateType::X509(X509CertificateChain { - certificate_array: vec![Vec::from(CERT_1_BUFFER.as_ref()), Vec::from(CERT_2_BUFFER.as_ref())], - }), + preamble_message_type: PreambleType::LicenseRequest, + preamble_flags: PreambleFlags::empty(), + preamble_version: PreambleVersion::V3, + preamble_message_size: 0, + }, + server_random: Vec::from(SERVER_RANDOM_BUFFER.as_ref()), + product_info: ProductInfo { + version: 0x60000, + company_name: "Microsoft Corporation".to_owned(), + product_id: "A02".to_owned(), + }, + server_certificate: Some(ServerCertificate { + issued_permanently: true, + certificate: CertificateType::X509(X509CertificateChain { + certificate_array: vec![Vec::from(CERT_1_BUFFER.as_ref()), Vec::from(CERT_2_BUFFER.as_ref())], }), - scope_list: vec![Scope(String::from("microsoft.com"))], - }; - req.license_header.preamble_message_size = u16::try_from(req.size()).expect("can't panic"); - req.into() - }; - pub static ref X509_CERTIFICATE: ServerCertificate = ServerCertificate { - issued_permanently: true, - certificate: CertificateType::X509(X509CertificateChain { - certificate_array: vec![Vec::from(CERT_1_BUFFER.as_ref()), Vec::from(CERT_2_BUFFER.as_ref()),], }), + scope_list: vec![Scope(String::from("microsoft.com"))], }; - pub static ref SCOPE: Scope = Scope(String::from("microsoft.com")); - static ref CERT_CHAIN: X509CertificateChain = X509CertificateChain { - certificate_array: vec![Vec::from(CERT_1_BUFFER.as_ref()), Vec::from(CERT_2_BUFFER.as_ref()),], - }; -} + req.license_header.preamble_message_size = u16::try_from(req.size()).expect("can't panic"); + req.into() +}); +static X509_CERTIFICATE: LazyLock = LazyLock::new(|| ServerCertificate { + issued_permanently: true, + certificate: CertificateType::X509(X509CertificateChain { + certificate_array: vec![Vec::from(CERT_1_BUFFER.as_ref()), Vec::from(CERT_2_BUFFER.as_ref())], + }), +}); +static SCOPE: LazyLock = LazyLock::new(|| Scope(String::from("microsoft.com"))); +static CERT_CHAIN: LazyLock = LazyLock::new(|| X509CertificateChain { + certificate_array: vec![Vec::from(CERT_1_BUFFER.as_ref()), Vec::from(CERT_2_BUFFER.as_ref())], +}); #[test] fn from_buffer_correctly_parses_server_license_request() { diff --git a/crates/ironrdp-pdu/src/rdp/server_license/server_platform_challenge/test.rs b/crates/ironrdp-pdu/src/rdp/server_license/server_platform_challenge/test.rs index 7de7fa56..3939b1d4 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/server_platform_challenge/test.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/server_platform_challenge/test.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::*; use crate::rdp::server_license::{ @@ -26,8 +27,8 @@ const MAC_DATA_BUFFER: [u8; MAC_SIZE] = [ 0x38, 0x23, 0x62, 0x5d, 0x10, 0x8b, 0x93, 0xc3, 0xf1, 0xe4, 0x67, 0x1f, 0x4a, 0xb6, 0x00, 0x0a, // mac data ]; -lazy_static! { - pub static ref PLATFORM_CHALLENGE: LicensePdu = ServerPlatformChallenge { +static PLATFORM_CHALLENGE: LazyLock = LazyLock::new(|| { + ServerPlatformChallenge { license_header: LicenseHeader { security_header: BasicSecurityHeader { flags: BasicSecurityHeaderFlags::LICENSE_PKT, @@ -41,8 +42,8 @@ lazy_static! { encrypted_platform_challenge: Vec::from(CHALLENGE_BUFFER.as_ref()), mac_data: Vec::from(MAC_DATA_BUFFER.as_ref()), } - .into(); -} + .into() +}); #[test] fn from_buffer_correctly_parses_server_platform_challenge() { diff --git a/crates/ironrdp-pdu/src/rdp/server_license/server_upgrade_license/tests.rs b/crates/ironrdp-pdu/src/rdp/server_license/server_upgrade_license/tests.rs index 3f4a5f24..a89d31c0 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/server_upgrade_license/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/server_upgrade_license/tests.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::*; use crate::rdp::server_license::{ @@ -244,15 +245,15 @@ const NEW_LICENSE_INFORMATION_BUFFER: [u8; 2031] = [ 0xb8, 0x1b, 0xb9, 0xcd, 0xfb, 0x31, 0x00, // license info ]; -lazy_static! { - pub static ref NEW_LICENSE_INFORMATION: LicenseInformation = LicenseInformation { - version: 0x0006_0000, - scope: "microsoft.com".to_owned(), - company_name: "Microsoft Corporation".to_owned(), - product_id: "A02".to_owned(), - license_info: Vec::from(&NEW_LICENSE_INFORMATION_BUFFER[NEW_LICENSE_INFORMATION_BUFFER.len() - 0x0799..]), - }; - pub static ref SERVER_UPGRADE_LICENSE: LicensePdu = ServerUpgradeLicense { +static NEW_LICENSE_INFORMATION: LazyLock = LazyLock::new(|| LicenseInformation { + version: 0x0006_0000, + scope: "microsoft.com".to_owned(), + company_name: "Microsoft Corporation".to_owned(), + product_id: "A02".to_owned(), + license_info: Vec::from(&NEW_LICENSE_INFORMATION_BUFFER[NEW_LICENSE_INFORMATION_BUFFER.len() - 0x0799..]), +}); +static SERVER_UPGRADE_LICENSE: LazyLock = LazyLock::new(|| { + ServerUpgradeLicense { license_header: LicenseHeader { security_header: BasicSecurityHeader { flags: BasicSecurityHeaderFlags::LICENSE_PKT, @@ -264,12 +265,12 @@ lazy_static! { .expect("buffer size is too large"), }, encrypted_license_info: Vec::from( - &SERVER_UPGRADE_LICENSE_BUFFER[12..SERVER_UPGRADE_LICENSE_BUFFER.len() - MAC_SIZE] + &SERVER_UPGRADE_LICENSE_BUFFER[12..SERVER_UPGRADE_LICENSE_BUFFER.len() - MAC_SIZE], ), mac_data: Vec::from(MAC_DATA.as_ref()), } - .into(); -} + .into() +}); #[test] fn from_buffer_correctly_parses_new_license_information() { diff --git a/crates/ironrdp-pdu/src/rdp/server_license/tests.rs b/crates/ironrdp-pdu/src/rdp/server_license/tests.rs index c4880a7a..84bbb408 100644 --- a/crates/ironrdp-pdu/src/rdp/server_license/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/server_license/tests.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::*; @@ -35,17 +36,15 @@ const STATUS_VALID_CLIENT_BUFFER: [u8; 20] = [ 0xff, 0x03, 0x10, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, ]; -lazy_static! { - pub static ref LICENSE_HEADER: LicenseHeader = LicenseHeader { - security_header: BasicSecurityHeader { - flags: BasicSecurityHeaderFlags::LICENSE_PKT, - }, - preamble_message_type: PreambleType::ErrorAlert, - preamble_flags: PreambleFlags::empty(), - preamble_version: PreambleVersion::V3, - preamble_message_size: 0x10, - }; -} +static LICENSE_HEADER: LazyLock = LazyLock::new(|| LicenseHeader { + security_header: BasicSecurityHeader { + flags: BasicSecurityHeaderFlags::LICENSE_PKT, + }, + preamble_message_type: PreambleType::ErrorAlert, + preamble_flags: PreambleFlags::empty(), + preamble_version: PreambleVersion::V3, + preamble_message_size: 0x10, +}); #[test] fn read_blob_header_handles_wrong_type_correctly() { diff --git a/crates/ironrdp-pdu/src/rdp/session_info/tests.rs b/crates/ironrdp-pdu/src/rdp/session_info/tests.rs index b230f6d2..2af92759 100644 --- a/crates/ironrdp-pdu/src/rdp/session_info/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/session_info/tests.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec, DecodeErrorKind}; -use lazy_static::lazy_static; use super::*; @@ -234,39 +235,37 @@ const DOMAIN_NAME: &str = "NTDEV"; const USER_NAME: &str = "eltons"; const SESSION_ID: u32 = 0x02; -lazy_static! { - static ref LOGON_INFO_V1: LogonInfoVersion1 = LogonInfoVersion1 { - logon_info: LogonInfo { - domain_name: DOMAIN_NAME.to_owned(), - user_name: USER_NAME.to_owned(), - session_id: SESSION_ID, - }, - }; - static ref LOGON_INFO_V2: LogonInfoVersion2 = LogonInfoVersion2 { - logon_info: LogonInfo { - domain_name: DOMAIN_NAME.to_owned(), - user_name: USER_NAME.to_owned(), - session_id: SESSION_ID, - }, - }; - static ref LOGON_EXTENDED: LogonInfoExtended = LogonInfoExtended { - present_fields_flags: LogonExFlags::AUTO_RECONNECT_COOKIE | LogonExFlags::LOGON_ERRORS, - auto_reconnect: Some(ServerAutoReconnect { - logon_id: SESSION_ID, - random_bits: [ - 0xa8, 0x02, 0xe7, 0x25, 0xe2, 0x4c, 0x82, 0xb7, 0x52, 0xa5, 0x53, 0x50, 0x34, 0x98, 0xa1, 0xa8 - ], - }), - errors_info: Some(LogonErrorsInfo { - error_type: LogonErrorNotificationType::NoPermission, - error_data: LogonErrorNotificationData::ErrorCode(LogonErrorNotificationDataErrorCode::FailedOther), - }), - }; - static ref SESSION_PLAIN_NOTIFY: SaveSessionInfoPdu = SaveSessionInfoPdu { - info_type: InfoType::PlainNotify, - info_data: InfoData::PlainNotify, - }; -} +static LOGON_INFO_V1: LazyLock = LazyLock::new(|| LogonInfoVersion1 { + logon_info: LogonInfo { + domain_name: DOMAIN_NAME.to_owned(), + user_name: USER_NAME.to_owned(), + session_id: SESSION_ID, + }, +}); +static LOGON_INFO_V2: LazyLock = LazyLock::new(|| LogonInfoVersion2 { + logon_info: LogonInfo { + domain_name: DOMAIN_NAME.to_owned(), + user_name: USER_NAME.to_owned(), + session_id: SESSION_ID, + }, +}); +static LOGON_EXTENDED: LazyLock = LazyLock::new(|| LogonInfoExtended { + present_fields_flags: LogonExFlags::AUTO_RECONNECT_COOKIE | LogonExFlags::LOGON_ERRORS, + auto_reconnect: Some(ServerAutoReconnect { + logon_id: SESSION_ID, + random_bits: [ + 0xa8, 0x02, 0xe7, 0x25, 0xe2, 0x4c, 0x82, 0xb7, 0x52, 0xa5, 0x53, 0x50, 0x34, 0x98, 0xa1, 0xa8, + ], + }), + errors_info: Some(LogonErrorsInfo { + error_type: LogonErrorNotificationType::NoPermission, + error_data: LogonErrorNotificationData::ErrorCode(LogonErrorNotificationDataErrorCode::FailedOther), + }), +}); +static SESSION_PLAIN_NOTIFY: LazyLock = LazyLock::new(|| SaveSessionInfoPdu { + info_type: InfoType::PlainNotify, + info_data: InfoData::PlainNotify, +}); #[test] fn from_buffer_correct_parses_logon_info_v1() { diff --git a/crates/ironrdp-pdu/src/rdp/vc/tests.rs b/crates/ironrdp-pdu/src/rdp/vc/tests.rs index b781bc4e..ef31a096 100644 --- a/crates/ironrdp-pdu/src/rdp/vc/tests.rs +++ b/crates/ironrdp-pdu/src/rdp/vc/tests.rs @@ -1,17 +1,16 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode, encode_vec}; -use lazy_static::lazy_static; use super::*; const CHANNEL_CHUNK_LENGTH_DEFAULT: u32 = 1600; const CHANNEL_PDU_HEADER_BUFFER: [u8; CHANNEL_PDU_HEADER_SIZE] = [0x40, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00]; -lazy_static! { - static ref CHANNEL_PDU_HEADER: ChannelPduHeader = ChannelPduHeader { - length: CHANNEL_CHUNK_LENGTH_DEFAULT, - flags: ChannelControlFlags::FLAG_FIRST, - }; -} +static CHANNEL_PDU_HEADER: LazyLock = LazyLock::new(|| ChannelPduHeader { + length: CHANNEL_CHUNK_LENGTH_DEFAULT, + flags: ChannelControlFlags::FLAG_FIRST, +}); #[test] fn from_buffer_correct_parses_channel_header() { diff --git a/crates/ironrdp-testsuite-core/Cargo.toml b/crates/ironrdp-testsuite-core/Cargo.toml index 217cc8a9..e2e29a1d 100644 --- a/crates/ironrdp-testsuite-core/Cargo.toml +++ b/crates/ironrdp-testsuite-core/Cargo.toml @@ -26,7 +26,6 @@ array-concat = "0.5" expect-test = "1" ironrdp-core.path = "../ironrdp-core" ironrdp-pdu.path = "../ironrdp-pdu" -lazy_static.workspace = true # TODO: remove in favor of https://doc.rust-lang.org/std/sync/struct.OnceLock.html paste = "1" visibility = { version = "0.1", optional = true } diff --git a/crates/ironrdp-testsuite-core/src/capsets.rs b/crates/ironrdp-testsuite-core/src/capsets.rs index be4c8b56..1f9974d6 100644 --- a/crates/ironrdp-testsuite-core/src/capsets.rs +++ b/crates/ironrdp-testsuite-core/src/capsets.rs @@ -1,8 +1,9 @@ +use std::sync::LazyLock; + use ironrdp_core::decode; use ironrdp_pdu::rdp::capability_sets::{ CapabilitySet, ClientConfirmActive, DemandActive, ServerDemandActive, SERVER_CHANNEL_ID, }; -use lazy_static::lazy_static; pub const SERVER_DEMAND_ACTIVE_BUFFER: [u8; 357] = [ 0x04, 0x00, // source descriptor length @@ -264,28 +265,29 @@ pub const CLIENT_MULTI_FRAGMENT_UPDATE_CAPABILITY_SET: [u8; 4] = [0x0, 0x0, 0x0, pub const CLIENT_WINDOW_LIST_CAPABILITY_SET: [u8; 7] = [0x1, 0x0, 0x0, 0x0, 0x3, 0xc, 0x0]; -lazy_static! { - pub static ref SERVER_DEMAND_ACTIVE: ServerDemandActive = ServerDemandActive { - pdu: DemandActive { - source_descriptor: String::from("RDP"), - capability_sets: vec![ - CapabilitySet::Share(SERVER_SHARE_CAPABILITY_SET.to_vec()), - CapabilitySet::General(decode(SERVER_GENERAL_CAPABILITY_SET.as_ref()).unwrap()), - CapabilitySet::VirtualChannel(decode(SERVER_VIRTUAL_CHANNEL_CAPABILITY_SET.as_ref()).unwrap()), - CapabilitySet::DrawGdiPlus(SERVER_DRAW_GDI_PLUS_CAPABILITY_SET.to_vec()), - CapabilitySet::Font(SERVER_FONT_CAPABILITY_SET.to_vec()), - CapabilitySet::Bitmap(decode(SERVER_BITMAP_CAPABILITY_SET.as_ref()).unwrap()), - CapabilitySet::Order(decode(SERVER_ORDER_CAPABILITY_SET.as_ref()).unwrap()), - CapabilitySet::ColorCache(SERVER_COLOR_CACHE_CAPABILITY_SET.to_vec()), - CapabilitySet::BitmapCacheHostSupport(SERVER_BITMAP_CACHE_HOST_SUPPORT_CAPABILITY_SET.to_vec()), - CapabilitySet::Pointer(decode(SERVER_POINTER_CAPABILITY_SET.as_ref()).unwrap()), - CapabilitySet::Input(decode(SERVER_INPUT_CAPABILITY_SET.as_ref()).unwrap()), - CapabilitySet::Rail(SERVER_RAIL_CAPABILITY_SET.to_vec()), - CapabilitySet::WindowList(SERVER_WINDOW_LIST_CAPABILITY_SET.to_vec()), - ], - } - }; - pub static ref CLIENT_DEMAND_ACTIVE_WITH_INCOMPLETE_CAPABILITY_SET: ClientConfirmActive = ClientConfirmActive { +pub static SERVER_DEMAND_ACTIVE: LazyLock = LazyLock::new(|| ServerDemandActive { + pdu: DemandActive { + source_descriptor: String::from("RDP"), + capability_sets: vec![ + CapabilitySet::Share(SERVER_SHARE_CAPABILITY_SET.to_vec()), + CapabilitySet::General(decode(SERVER_GENERAL_CAPABILITY_SET.as_ref()).unwrap()), + CapabilitySet::VirtualChannel(decode(SERVER_VIRTUAL_CHANNEL_CAPABILITY_SET.as_ref()).unwrap()), + CapabilitySet::DrawGdiPlus(SERVER_DRAW_GDI_PLUS_CAPABILITY_SET.to_vec()), + CapabilitySet::Font(SERVER_FONT_CAPABILITY_SET.to_vec()), + CapabilitySet::Bitmap(decode(SERVER_BITMAP_CAPABILITY_SET.as_ref()).unwrap()), + CapabilitySet::Order(decode(SERVER_ORDER_CAPABILITY_SET.as_ref()).unwrap()), + CapabilitySet::ColorCache(SERVER_COLOR_CACHE_CAPABILITY_SET.to_vec()), + CapabilitySet::BitmapCacheHostSupport(SERVER_BITMAP_CACHE_HOST_SUPPORT_CAPABILITY_SET.to_vec()), + CapabilitySet::Pointer(decode(SERVER_POINTER_CAPABILITY_SET.as_ref()).unwrap()), + CapabilitySet::Input(decode(SERVER_INPUT_CAPABILITY_SET.as_ref()).unwrap()), + CapabilitySet::Rail(SERVER_RAIL_CAPABILITY_SET.to_vec()), + CapabilitySet::WindowList(SERVER_WINDOW_LIST_CAPABILITY_SET.to_vec()), + ], + }, +}); + +pub static CLIENT_DEMAND_ACTIVE_WITH_INCOMPLETE_CAPABILITY_SET: LazyLock = + LazyLock::new(|| ClientConfirmActive { originator_id: SERVER_CHANNEL_ID, pdu: DemandActive { source_descriptor: String::from("MSTSC"), @@ -306,41 +308,41 @@ lazy_static! { CapabilitySet::Brush(decode(CLIENT_BRUSH_CAPABILITY_SET.as_ref()).unwrap()), CapabilitySet::OffscreenBitmapCache(decode(CLIENT_OFFSCREEN_BITMAP_CAPABILITY_SET.as_ref()).unwrap()), CapabilitySet::VirtualChannel( - decode(CLIENT_VIRTUAL_CHANNEL_CAPABILITY_SET_INCOMPLETE.as_ref()).unwrap() + decode(CLIENT_VIRTUAL_CHANNEL_CAPABILITY_SET_INCOMPLETE.as_ref()).unwrap(), ), CapabilitySet::DrawNineGridCache(CLIENT_DRAW_NINE_GRID_CACHE_CAPABILITY_SET.to_vec()), CapabilitySet::DrawGdiPlus(CLIENT_DRAW_GDI_PLUS_CAPABILITY_SET.to_vec()), CapabilitySet::MultiFragmentUpdate( - decode(CLIENT_MULTI_FRAGMENT_UPDATE_CAPABILITY_SET.as_ref()).unwrap() + decode(CLIENT_MULTI_FRAGMENT_UPDATE_CAPABILITY_SET.as_ref()).unwrap(), ), CapabilitySet::WindowList(CLIENT_WINDOW_LIST_CAPABILITY_SET.to_vec()), ], - } - }; - pub static ref CLIENT_DEMAND_ACTIVE: ClientConfirmActive = ClientConfirmActive { - originator_id: SERVER_CHANNEL_ID, - pdu: DemandActive { - source_descriptor: String::from("MSTSC"), - capability_sets: vec![ - CapabilitySet::General(decode(CLIENT_GENERAL_CAPABILITY_SET.as_ref()).unwrap()), - CapabilitySet::Bitmap(decode(CLIENT_BITMAP_CAPABILITY_SET.as_ref()).unwrap()), - CapabilitySet::Order(decode(CLIENT_ORDER_CAPABILITY_SET.as_ref()).unwrap()), - CapabilitySet::BitmapCacheRev2(decode(CLIENT_BITMAP_CACHE_REV_2_CAPABILITY_SET.as_ref()).unwrap()), - CapabilitySet::ColorCache(CLIENT_COLOR_CACHE_CAPABILITY_SET.to_vec()), - CapabilitySet::WindowActivation(CLIENT_WINDOW_ACTIVATION_CAPABILITY_SET.to_vec()), - CapabilitySet::Control(CLIENT_CONTROL_CAPABILITY_SET.to_vec()), - CapabilitySet::Pointer(decode(CLIENT_POINTER_CAPABILITY_SET.as_ref()).unwrap()), - CapabilitySet::Share(CLIENT_SHARE_CAPABILITY_SET.to_vec()), - CapabilitySet::Input(decode(CLIENT_INPUT_CAPABILITY_SET.as_ref()).unwrap()), - CapabilitySet::Sound(decode(CLIENT_SOUND_CAPABILITY_SET.as_ref()).unwrap()), - CapabilitySet::Font(CLIENT_FONT_CAPABILITY_SET.to_vec()), - CapabilitySet::GlyphCache(decode(CLIENT_GLYPH_CACHE_CAPABILITY_SET.as_ref()).unwrap()), - CapabilitySet::Brush(decode(CLIENT_BRUSH_CAPABILITY_SET.as_ref()).unwrap()), - CapabilitySet::OffscreenBitmapCache(decode(CLIENT_OFFSCREEN_BITMAP_CAPABILITY_SET.as_ref()).unwrap()), - CapabilitySet::VirtualChannel(decode(CLIENT_VIRTUAL_CHANNEL_CAPABILITY_SET.as_ref()).unwrap()), - CapabilitySet::DrawNineGridCache(CLIENT_DRAW_NINE_GRID_CACHE_CAPABILITY_SET.to_vec()), - CapabilitySet::DrawGdiPlus(CLIENT_DRAW_GDI_PLUS_CAPABILITY_SET.to_vec()), - ], - } - }; -} + }, + }); + +pub static CLIENT_DEMAND_ACTIVE: LazyLock = LazyLock::new(|| ClientConfirmActive { + originator_id: SERVER_CHANNEL_ID, + pdu: DemandActive { + source_descriptor: String::from("MSTSC"), + capability_sets: vec![ + CapabilitySet::General(decode(CLIENT_GENERAL_CAPABILITY_SET.as_ref()).unwrap()), + CapabilitySet::Bitmap(decode(CLIENT_BITMAP_CAPABILITY_SET.as_ref()).unwrap()), + CapabilitySet::Order(decode(CLIENT_ORDER_CAPABILITY_SET.as_ref()).unwrap()), + CapabilitySet::BitmapCacheRev2(decode(CLIENT_BITMAP_CACHE_REV_2_CAPABILITY_SET.as_ref()).unwrap()), + CapabilitySet::ColorCache(CLIENT_COLOR_CACHE_CAPABILITY_SET.to_vec()), + CapabilitySet::WindowActivation(CLIENT_WINDOW_ACTIVATION_CAPABILITY_SET.to_vec()), + CapabilitySet::Control(CLIENT_CONTROL_CAPABILITY_SET.to_vec()), + CapabilitySet::Pointer(decode(CLIENT_POINTER_CAPABILITY_SET.as_ref()).unwrap()), + CapabilitySet::Share(CLIENT_SHARE_CAPABILITY_SET.to_vec()), + CapabilitySet::Input(decode(CLIENT_INPUT_CAPABILITY_SET.as_ref()).unwrap()), + CapabilitySet::Sound(decode(CLIENT_SOUND_CAPABILITY_SET.as_ref()).unwrap()), + CapabilitySet::Font(CLIENT_FONT_CAPABILITY_SET.to_vec()), + CapabilitySet::GlyphCache(decode(CLIENT_GLYPH_CACHE_CAPABILITY_SET.as_ref()).unwrap()), + CapabilitySet::Brush(decode(CLIENT_BRUSH_CAPABILITY_SET.as_ref()).unwrap()), + CapabilitySet::OffscreenBitmapCache(decode(CLIENT_OFFSCREEN_BITMAP_CAPABILITY_SET.as_ref()).unwrap()), + CapabilitySet::VirtualChannel(decode(CLIENT_VIRTUAL_CHANNEL_CAPABILITY_SET.as_ref()).unwrap()), + CapabilitySet::DrawNineGridCache(CLIENT_DRAW_NINE_GRID_CACHE_CAPABILITY_SET.to_vec()), + CapabilitySet::DrawGdiPlus(CLIENT_DRAW_GDI_PLUS_CAPABILITY_SET.to_vec()), + ], + }, +}); diff --git a/crates/ironrdp-testsuite-core/src/client_info.rs b/crates/ironrdp-testsuite-core/src/client_info.rs index 2db443c6..12c21b11 100644 --- a/crates/ironrdp-testsuite-core/src/client_info.rs +++ b/crates/ironrdp-testsuite-core/src/client_info.rs @@ -1,3 +1,5 @@ +use std::sync::LazyLock; + use ironrdp_pdu::rdp::client_info::{ AddressFamily, ClientInfo, ClientInfoFlags, CompressionType, Credentials, DayOfWeek, DayOfWeekOccurrence, ExtendedClientInfo, ExtendedClientOptionalInfo, Month, OptionalSystemTime, PerformanceFlags, SystemTime, @@ -81,75 +83,73 @@ pub const CLIENT_INFO_BUFFER_ANSI: [u8; 301] = [ 0x01, 0x00, 0x00, 0x00, // performance flags ]; -lazy_static::lazy_static! { - pub static ref CLIENT_INFO_UNICODE: ClientInfo = ClientInfo { - code_page: 0x0409_0409, - flags: ClientInfoFlags::MOUSE - | ClientInfoFlags::DISABLE_CTRL_ALT_DEL - | ClientInfoFlags::UNICODE - | ClientInfoFlags::MAXIMIZE_SHELL - | ClientInfoFlags::COMPRESSION - | ClientInfoFlags::ENABLE_WINDOWS_KEY - | ClientInfoFlags::FORCE_ENCRYPTED_CS_PDU, - compression_type: CompressionType::K64, - credentials: Credentials { - username: String::from("eltons"), - password: String::from(""), - domain: Some(String::from("NTDEV")) - }, - alternate_shell: String::from(""), - work_dir: String::from(""), - extra_info: ExtendedClientInfo { - address_family: AddressFamily::INET, - address: String::from("157.59.242.156"), - dir: String::from("C:\\depots\\w2k3_1\\termsrv\\newclient\\lib\\win32\\obj\\i386\\mstscax.dll"), - optional_data: ExtendedClientOptionalInfo::builder() - .timezone(TimezoneInfo { - bias: 480, - standard_name: String::from("Pacific Standard Time"), - standard_date: OptionalSystemTime(Some(SystemTime { - month: Month::October, - day_of_week: DayOfWeek::Sunday, - day: DayOfWeekOccurrence::Last, - hour: 2, - minute: 0, - second: 0, - milliseconds: 0, - })), - standard_bias: 0, - daylight_name: String::from("Pacific Daylight Time"), - daylight_date: OptionalSystemTime(Some(SystemTime { - month: Month::April, - day_of_week: DayOfWeek::Sunday, - day: DayOfWeekOccurrence::First, - hour: 2, - minute: 0, - second: 0, - milliseconds: 0, - })), - daylight_bias: -60, - }) - .session_id(0) - .performance_flags(PerformanceFlags::DISABLE_WALLPAPER) - .build(), - }, - }; +pub static CLIENT_INFO_UNICODE: LazyLock = LazyLock::new(|| ClientInfo { + code_page: 0x0409_0409, + flags: ClientInfoFlags::MOUSE + | ClientInfoFlags::DISABLE_CTRL_ALT_DEL + | ClientInfoFlags::UNICODE + | ClientInfoFlags::MAXIMIZE_SHELL + | ClientInfoFlags::COMPRESSION + | ClientInfoFlags::ENABLE_WINDOWS_KEY + | ClientInfoFlags::FORCE_ENCRYPTED_CS_PDU, + compression_type: CompressionType::K64, + credentials: Credentials { + username: String::from("eltons"), + password: String::from(""), + domain: Some(String::from("NTDEV")), + }, + alternate_shell: String::from(""), + work_dir: String::from(""), + extra_info: ExtendedClientInfo { + address_family: AddressFamily::INET, + address: String::from("157.59.242.156"), + dir: String::from("C:\\depots\\w2k3_1\\termsrv\\newclient\\lib\\win32\\obj\\i386\\mstscax.dll"), + optional_data: ExtendedClientOptionalInfo::builder() + .timezone(TimezoneInfo { + bias: 480, + standard_name: String::from("Pacific Standard Time"), + standard_date: OptionalSystemTime(Some(SystemTime { + month: Month::October, + day_of_week: DayOfWeek::Sunday, + day: DayOfWeekOccurrence::Last, + hour: 2, + minute: 0, + second: 0, + milliseconds: 0, + })), + standard_bias: 0, + daylight_name: String::from("Pacific Daylight Time"), + daylight_date: OptionalSystemTime(Some(SystemTime { + month: Month::April, + day_of_week: DayOfWeek::Sunday, + day: DayOfWeekOccurrence::First, + hour: 2, + minute: 0, + second: 0, + milliseconds: 0, + })), + daylight_bias: -60, + }) + .session_id(0) + .performance_flags(PerformanceFlags::DISABLE_WALLPAPER) + .build(), + }, +}); - pub static ref CLIENT_INFO_ANSI: ClientInfo = { - let mut client_info = CLIENT_INFO_UNICODE.clone(); - client_info.flags -= ClientInfoFlags::UNICODE; - client_info - }; +pub static CLIENT_INFO_ANSI: LazyLock = LazyLock::new(|| { + let mut client_info = CLIENT_INFO_UNICODE.clone(); + client_info.flags -= ClientInfoFlags::UNICODE; + client_info +}); - pub static ref CLIENT_INFO_UNICODE_WITHOUT_OPTIONAL_FIELDS: ClientInfo = { - let mut client_info = CLIENT_INFO_UNICODE.clone(); - client_info.extra_info.optional_data = ExtendedClientOptionalInfo::default(); - client_info - }; +pub static CLIENT_INFO_UNICODE_WITHOUT_OPTIONAL_FIELDS: LazyLock = LazyLock::new(|| { + let mut client_info = CLIENT_INFO_UNICODE.clone(); + client_info.extra_info.optional_data = ExtendedClientOptionalInfo::default(); + client_info +}); - pub static ref CLIENT_INFO_BUFFER_UNICODE_WITHOUT_OPTIONAL_FIELDS: Vec = { - let mut buffer = CLIENT_INFO_BUFFER_UNICODE.to_vec(); - buffer.truncate(CLIENT_INFO_BUFFER_UNICODE_WITHOUT_OPTIONAL_FIELDS_LEN); - buffer - }; -} +pub static CLIENT_INFO_BUFFER_UNICODE_WITHOUT_OPTIONAL_FIELDS: LazyLock> = LazyLock::new(|| { + let mut buffer = CLIENT_INFO_BUFFER_UNICODE.to_vec(); + buffer.truncate(CLIENT_INFO_BUFFER_UNICODE_WITHOUT_OPTIONAL_FIELDS_LEN); + buffer +}); diff --git a/crates/ironrdp-testsuite-core/src/cluster_data.rs b/crates/ironrdp-testsuite-core/src/cluster_data.rs index 7729c00f..165430a9 100644 --- a/crates/ironrdp-testsuite-core/src/cluster_data.rs +++ b/crates/ironrdp-testsuite-core/src/cluster_data.rs @@ -1,12 +1,11 @@ +use std::sync::LazyLock; + use ironrdp_pdu::gcc::{ClientClusterData, RedirectionFlags, RedirectionVersion}; -use lazy_static::lazy_static; pub const CLUSTER_DATA_BUFFER: [u8; 8] = [0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; -lazy_static! { - pub static ref CLUSTER_DATA: ClientClusterData = ClientClusterData { - flags: RedirectionFlags::REDIRECTION_SUPPORTED, - redirection_version: RedirectionVersion::V4, - redirected_session_id: 0, - }; -} +pub static CLUSTER_DATA: LazyLock = LazyLock::new(|| ClientClusterData { + flags: RedirectionFlags::REDIRECTION_SUPPORTED, + redirection_version: RedirectionVersion::V4, + redirected_session_id: 0, +}); diff --git a/crates/ironrdp-testsuite-core/src/conference_create.rs b/crates/ironrdp-testsuite-core/src/conference_create.rs index d90398fb..92ab3344 100644 --- a/crates/ironrdp-testsuite-core/src/conference_create.rs +++ b/crates/ironrdp-testsuite-core/src/conference_create.rs @@ -1,6 +1,7 @@ +use std::sync::LazyLock; + use array_concat::{concat_arrays, concat_arrays_size}; use ironrdp_pdu::gcc::{ConferenceCreateRequest, ConferenceCreateResponse}; -use lazy_static::lazy_static; use crate::gcc; @@ -14,13 +15,12 @@ pub const CONFERENCE_CREATE_RESPONSE_PREFIX_BUFFER: [u8; 24] = [ 0x63, 0x44, 0x6e, 0x81, 0x08, ]; -lazy_static! { - pub static ref CONFERENCE_CREATE_REQUEST: ConferenceCreateRequest = - ConferenceCreateRequest::new(gcc::CLIENT_GCC_WITH_CLUSTER_OPTIONAL_FIELD.clone()).expect("should not fail"); - pub static ref CONFERENCE_CREATE_RESPONSE: ConferenceCreateResponse = - ConferenceCreateResponse::new(0x79f3, gcc::SERVER_GCC_WITHOUT_OPTIONAL_FIELDS.clone(),) - .expect("should not fail"); -} +pub static CONFERENCE_CREATE_REQUEST: LazyLock = LazyLock::new(|| { + ConferenceCreateRequest::new(gcc::CLIENT_GCC_WITH_CLUSTER_OPTIONAL_FIELD.clone()).expect("should not fail") +}); +pub static CONFERENCE_CREATE_RESPONSE: LazyLock = LazyLock::new(|| { + ConferenceCreateResponse::new(0x79f3, gcc::SERVER_GCC_WITHOUT_OPTIONAL_FIELDS.clone()).expect("should not fail") +}); pub const CONFERENCE_CREATE_REQUEST_BUFFER: [u8; concat_arrays_size!( CONFERENCE_CREATE_REQUEST_PREFIX_BUFFER, diff --git a/crates/ironrdp-testsuite-core/src/core_data.rs b/crates/ironrdp-testsuite-core/src/core_data.rs index e552727b..78a08d8f 100644 --- a/crates/ironrdp-testsuite-core/src/core_data.rs +++ b/crates/ironrdp-testsuite-core/src/core_data.rs @@ -1,3 +1,5 @@ +use std::sync::LazyLock; + use array_concat::{concat_arrays, concat_arrays_size}; use ironrdp_pdu::gcc::{ ClientCoreData, ClientCoreOptionalData, ClientEarlyCapabilityFlags, ColorDepth, ConnectionType, HighColorDepth, @@ -5,7 +7,6 @@ use ironrdp_pdu::gcc::{ SupportedColorDepths, }; use ironrdp_pdu::nego::SecurityProtocol; -use lazy_static::lazy_static; pub const CLIENT_CORE_DATA_BUFFER: [u8; 128] = [ 0x04, 0x00, 0x08, 0x00, // version @@ -57,55 +58,59 @@ pub const CLIENT_OPTIONAL_CORE_DATA_FROM_DESKTOP_PHYSICAL_WIDTH_TO_DEVICE_SCALE_ 0x8c, 0x00, 0x00, 0x00, // device scale factor ]; -lazy_static! { - pub static ref CLIENT_CORE_DATA_WITHOUT_OPTIONAL_FIELDS: ClientCoreData = ClientCoreData { - version: RdpVersion::V5_PLUS, - desktop_width: 1280, - desktop_height: 1024, - color_depth: ColorDepth::Bpp4, - sec_access_sequence: SecureAccessSequence::Del, - keyboard_layout: 1033, - client_build: 3790, - client_name: String::from("ELTONS-DEV2"), - keyboard_type: KeyboardType::IbmEnhanced, - keyboard_subtype: 0, - keyboard_functional_keys_count: 12, - ime_file_name: String::new(), - optional_data: ClientCoreOptionalData::default(), - }; - pub static ref CLIENT_OPTIONAL_CORE_DATA_TO_HIGH_COLOR_DEPTH: ClientCoreData = { - let mut data = CLIENT_CORE_DATA_WITHOUT_OPTIONAL_FIELDS.clone(); - data.optional_data.post_beta2_color_depth = Some(ColorDepth::Bpp8); - data.optional_data.client_product_id = Some(1); - data.optional_data.serial_number = Some(0); - data - }; - pub static ref CLIENT_OPTIONAL_CORE_DATA_TO_SERVER_SELECTED_PROTOCOL: ClientCoreData = { - let mut data = CLIENT_OPTIONAL_CORE_DATA_TO_HIGH_COLOR_DEPTH.clone(); - data.optional_data.high_color_depth = Some(HighColorDepth::Bpp24); - data.optional_data.supported_color_depths = - Some(SupportedColorDepths::BPP24 | SupportedColorDepths::BPP16 | SupportedColorDepths::BPP15); - data.optional_data.early_capability_flags = Some(ClientEarlyCapabilityFlags::SUPPORT_ERR_INFO_PDU); - data.optional_data.dig_product_id = Some(String::from("69712-783-0357974-42714")); - data.optional_data.connection_type = Some(ConnectionType::NotUsed); - data.optional_data.server_selected_protocol = Some(SecurityProtocol::empty()); - data - }; - pub static ref CLIENT_CORE_DATA_WITH_ALL_OPTIONAL_FIELDS: ClientCoreData = { - let mut data = CLIENT_OPTIONAL_CORE_DATA_TO_SERVER_SELECTED_PROTOCOL.clone(); - data.optional_data.desktop_physical_width = Some(5000); - data.optional_data.desktop_physical_height = Some(3000); - data.optional_data.desktop_orientation = Some(90); - data.optional_data.desktop_scale_factor = Some(200); - data.optional_data.device_scale_factor = Some(140); - data - }; - pub static ref CLIENT_CORE_DATA_WITH_ALL_OPTIONAL_FIELDS_WITH_WANT_32_BPP_EARLY_FLAG: ClientCoreData = { +pub static CLIENT_CORE_DATA_WITHOUT_OPTIONAL_FIELDS: LazyLock = LazyLock::new(|| ClientCoreData { + version: RdpVersion::V5_PLUS, + desktop_width: 1280, + desktop_height: 1024, + color_depth: ColorDepth::Bpp4, + sec_access_sequence: SecureAccessSequence::Del, + keyboard_layout: 1033, + client_build: 3790, + client_name: String::from("ELTONS-DEV2"), + keyboard_type: KeyboardType::IbmEnhanced, + keyboard_subtype: 0, + keyboard_functional_keys_count: 12, + ime_file_name: String::new(), + optional_data: ClientCoreOptionalData::default(), +}); + +pub static CLIENT_OPTIONAL_CORE_DATA_TO_HIGH_COLOR_DEPTH: LazyLock = LazyLock::new(|| { + let mut data = CLIENT_CORE_DATA_WITHOUT_OPTIONAL_FIELDS.clone(); + data.optional_data.post_beta2_color_depth = Some(ColorDepth::Bpp8); + data.optional_data.client_product_id = Some(1); + data.optional_data.serial_number = Some(0); + data +}); + +pub static CLIENT_OPTIONAL_CORE_DATA_TO_SERVER_SELECTED_PROTOCOL: LazyLock = LazyLock::new(|| { + let mut data = CLIENT_OPTIONAL_CORE_DATA_TO_HIGH_COLOR_DEPTH.clone(); + data.optional_data.high_color_depth = Some(HighColorDepth::Bpp24); + data.optional_data.supported_color_depths = + Some(SupportedColorDepths::BPP24 | SupportedColorDepths::BPP16 | SupportedColorDepths::BPP15); + data.optional_data.early_capability_flags = Some(ClientEarlyCapabilityFlags::SUPPORT_ERR_INFO_PDU); + data.optional_data.dig_product_id = Some(String::from("69712-783-0357974-42714")); + data.optional_data.connection_type = Some(ConnectionType::NotUsed); + data.optional_data.server_selected_protocol = Some(SecurityProtocol::empty()); + data +}); + +pub static CLIENT_CORE_DATA_WITH_ALL_OPTIONAL_FIELDS: LazyLock = LazyLock::new(|| { + let mut data = CLIENT_OPTIONAL_CORE_DATA_TO_SERVER_SELECTED_PROTOCOL.clone(); + data.optional_data.desktop_physical_width = Some(5000); + data.optional_data.desktop_physical_height = Some(3000); + data.optional_data.desktop_orientation = Some(90); + data.optional_data.desktop_scale_factor = Some(200); + data.optional_data.device_scale_factor = Some(140); + data +}); +pub static CLIENT_CORE_DATA_WITH_ALL_OPTIONAL_FIELDS_WITH_WANT_32_BPP_EARLY_FLAG: LazyLock = + LazyLock::new(|| { let mut data = CLIENT_CORE_DATA_WITH_ALL_OPTIONAL_FIELDS.clone(); data.optional_data.early_capability_flags = Some(ClientEarlyCapabilityFlags::WANT_32_BPP_SESSION); data - }; - pub static ref CLIENT_CORE_DATA_WITH_ALL_OPTIONAL_FIELDS_WITH_WANT_32_BPP_EARLY_FLAG_BUFFER: Vec = { + }); +pub static CLIENT_CORE_DATA_WITH_ALL_OPTIONAL_FIELDS_WITH_WANT_32_BPP_EARLY_FLAG_BUFFER: LazyLock> = + LazyLock::new(|| { let early_capability_flags = ClientEarlyCapabilityFlags::WANT_32_BPP_SESSION.bits().to_le_bytes(); let mut from_high_color_to_server_protocol = @@ -119,8 +124,7 @@ lazy_static! { buffer.extend(CLIENT_OPTIONAL_CORE_DATA_FROM_DESKTOP_PHYSICAL_WIDTH_TO_DEVICE_SCALE_FACTOR_BUFFER.as_ref()); buffer - }; -} + }); pub const CLIENT_OPTIONAL_CORE_DATA_TO_HIGH_COLOR_DEPTH_BUFFER_BUFFER: [u8; concat_arrays_size!( CLIENT_CORE_DATA_BUFFER, @@ -158,29 +162,28 @@ pub const FLAGS_BUFFER: [u8; 4] = [ 0x01, 0x00, 0x00, 0x00, // early capability flags ]; -lazy_static! { - pub static ref SERVER_CORE_DATA: ServerCoreData = ServerCoreData { - version: RdpVersion::V5_PLUS, - optional_data: ServerCoreOptionalData { - client_requested_protocols: None, - early_capability_flags: None, - }, - }; - pub static ref SERVER_CORE_DATA_TO_FLAGS: ServerCoreData = ServerCoreData { - version: RdpVersion::V5_PLUS, - optional_data: ServerCoreOptionalData { - client_requested_protocols: Some(SecurityProtocol::empty()), - early_capability_flags: None, - }, - }; - pub static ref SERVER_CORE_DATA_WITH_ALL_OPTIONAL_FIELDS: ServerCoreData = ServerCoreData { - version: RdpVersion::V5_PLUS, - optional_data: ServerCoreOptionalData { - client_requested_protocols: Some(SecurityProtocol::empty()), - early_capability_flags: Some(ServerEarlyCapabilityFlags::EDGE_ACTIONS_SUPPORTED_V1), - }, - }; -} +pub static SERVER_CORE_DATA: LazyLock = LazyLock::new(|| ServerCoreData { + version: RdpVersion::V5_PLUS, + optional_data: ServerCoreOptionalData { + client_requested_protocols: None, + early_capability_flags: None, + }, +}); +pub static SERVER_CORE_DATA_TO_FLAGS: LazyLock = LazyLock::new(|| ServerCoreData { + version: RdpVersion::V5_PLUS, + optional_data: ServerCoreOptionalData { + client_requested_protocols: Some(SecurityProtocol::empty()), + early_capability_flags: None, + }, +}); + +pub static SERVER_CORE_DATA_WITH_ALL_OPTIONAL_FIELDS: LazyLock = LazyLock::new(|| ServerCoreData { + version: RdpVersion::V5_PLUS, + optional_data: ServerCoreOptionalData { + client_requested_protocols: Some(SecurityProtocol::empty()), + early_capability_flags: Some(ServerEarlyCapabilityFlags::EDGE_ACTIONS_SUPPORTED_V1), + }, +}); pub const SERVER_CORE_DATA_TO_REQUESTED_PROTOCOL_BUFFER: [u8; concat_arrays_size!( SERVER_CORE_DATA_BUFFER, diff --git a/crates/ironrdp-testsuite-core/src/gcc.rs b/crates/ironrdp-testsuite-core/src/gcc.rs index 6c9a0466..d2fd9d2e 100644 --- a/crates/ironrdp-testsuite-core/src/gcc.rs +++ b/crates/ironrdp-testsuite-core/src/gcc.rs @@ -1,6 +1,7 @@ +use std::sync::LazyLock; + use array_concat::{concat_arrays, concat_arrays_size}; use ironrdp_pdu::gcc::{ClientGccBlocks, ClientGccType, ServerGccBlocks, ServerGccType}; -use lazy_static::lazy_static; use crate::cluster_data::{CLUSTER_DATA, CLUSTER_DATA_BUFFER}; use crate::core_data::{ @@ -125,42 +126,40 @@ pub const SERVER_GCC_WITH_OPTIONAL_FIELDS_IN_DIFFERENT_ORDER_BUFFER: [u8; concat SERVER_GCC_MULTI_TRANSPORT_CHANNEL_BLOCK_BUFFER ); -lazy_static! { - pub static ref CLIENT_GCC_WITHOUT_OPTIONAL_FIELDS: ClientGccBlocks = ClientGccBlocks { - core: CLIENT_OPTIONAL_CORE_DATA_TO_SERVER_SELECTED_PROTOCOL.clone(), - security: CLIENT_SECURITY_DATA.clone(), - network: Some(CLIENT_NETWORK_DATA_WITH_CHANNELS.clone()), - cluster: None, - monitor: None, - message_channel: None, - multi_transport_channel: None, - monitor_extended: None, - }; - pub static ref CLIENT_GCC_WITH_CLUSTER_OPTIONAL_FIELD: ClientGccBlocks = { - let mut data = CLIENT_GCC_WITHOUT_OPTIONAL_FIELDS.clone(); - data.cluster = Some(CLUSTER_DATA.clone()); - data - }; - pub static ref CLIENT_GCC_WITH_ALL_OPTIONAL_FIELDS: ClientGccBlocks = { - let mut data = CLIENT_GCC_WITH_CLUSTER_OPTIONAL_FIELD.clone(); - data.monitor = Some(crate::monitor_data::MONITOR_DATA_WITH_MONITORS.clone()); - data.monitor_extended = Some(crate::monitor_extended_data::MONITOR_DATA_WITH_MONITORS.clone()); - data - }; - pub static ref SERVER_GCC_WITHOUT_OPTIONAL_FIELDS: ServerGccBlocks = ServerGccBlocks { - core: SERVER_CORE_DATA_TO_FLAGS.clone(), - network: SERVER_NETWORK_DATA_WITH_CHANNELS_ID.clone(), - security: SERVER_SECURITY_DATA_WITH_OPTIONAL_FIELDS.clone(), - message_channel: None, - multi_transport_channel: None, - }; - pub static ref SERVER_GCC_WITH_OPTIONAL_FIELDS: ServerGccBlocks = { - let mut data = SERVER_GCC_WITHOUT_OPTIONAL_FIELDS.clone(); - data.message_channel = Some(SERVER_GCC_MESSAGE_CHANNEL_BLOCK.clone()); - data.multi_transport_channel = Some(SERVER_GCC_MULTI_TRANSPORT_CHANNEL_BLOCK.clone()); - data - }; -} +pub static CLIENT_GCC_WITHOUT_OPTIONAL_FIELDS: LazyLock = LazyLock::new(|| ClientGccBlocks { + core: CLIENT_OPTIONAL_CORE_DATA_TO_SERVER_SELECTED_PROTOCOL.clone(), + security: CLIENT_SECURITY_DATA.clone(), + network: Some(CLIENT_NETWORK_DATA_WITH_CHANNELS.clone()), + cluster: None, + monitor: None, + message_channel: None, + multi_transport_channel: None, + monitor_extended: None, +}); +pub static CLIENT_GCC_WITH_CLUSTER_OPTIONAL_FIELD: LazyLock = LazyLock::new(|| { + let mut data = CLIENT_GCC_WITHOUT_OPTIONAL_FIELDS.clone(); + data.cluster = Some(CLUSTER_DATA.clone()); + data +}); +pub static CLIENT_GCC_WITH_ALL_OPTIONAL_FIELDS: LazyLock = LazyLock::new(|| { + let mut data = CLIENT_GCC_WITH_CLUSTER_OPTIONAL_FIELD.clone(); + data.monitor = Some(crate::monitor_data::MONITOR_DATA_WITH_MONITORS.clone()); + data.monitor_extended = Some(crate::monitor_extended_data::MONITOR_DATA_WITH_MONITORS.clone()); + data +}); +pub static SERVER_GCC_WITHOUT_OPTIONAL_FIELDS: LazyLock = LazyLock::new(|| ServerGccBlocks { + core: SERVER_CORE_DATA_TO_FLAGS.clone(), + network: SERVER_NETWORK_DATA_WITH_CHANNELS_ID.clone(), + security: SERVER_SECURITY_DATA_WITH_OPTIONAL_FIELDS.clone(), + message_channel: None, + multi_transport_channel: None, +}); +pub static SERVER_GCC_WITH_OPTIONAL_FIELDS: LazyLock = LazyLock::new(|| { + let mut data = SERVER_GCC_WITHOUT_OPTIONAL_FIELDS.clone(); + data.message_channel = Some(SERVER_GCC_MESSAGE_CHANNEL_BLOCK.clone()); + data.multi_transport_channel = Some(SERVER_GCC_MULTI_TRANSPORT_CHANNEL_BLOCK.clone()); + data +}); pub const CLIENT_GCC_CORE_BLOCK_BUFFER: [u8; gcc_block_size( CLIENT_OPTIONAL_CORE_DATA_TO_SERVER_SELECTED_PROTOCOL_BUFFER, diff --git a/crates/ironrdp-testsuite-core/src/gfx.rs b/crates/ironrdp-testsuite-core/src/gfx.rs index 9adf797f..b9616048 100644 --- a/crates/ironrdp-testsuite-core/src/gfx.rs +++ b/crates/ironrdp-testsuite-core/src/gfx.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_pdu::rdp::vc::dvc::gfx::{ClientPdu, ServerPdu}; -use lazy_static::lazy_static; use crate::graphics_messages::{ FRAME_ACKNOWLEDGE, FRAME_ACKNOWLEDGE_BUFFER, WIRE_TO_SURFACE_1, WIRE_TO_SURFACE_1_BUFFER, @@ -8,11 +9,11 @@ use crate::graphics_messages::{ pub const WIRE_TO_SURFACE_1_HEADER_BUFFER: [u8; 8] = [0x01, 0x00, 0x00, 0x00, 0xe2, 0x00, 0x00, 0x00]; pub const FRAME_ACKNOWLEDGE_HEADER_BUFFER: [u8; 8] = [0x0d, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00]; -lazy_static! { - pub static ref HEADER_WITH_WIRE_TO_SURFACE_1_BUFFER: Vec = - [&WIRE_TO_SURFACE_1_HEADER_BUFFER[..], &WIRE_TO_SURFACE_1_BUFFER[..],].concat(); - pub static ref HEADER_WITH_FRAME_ACKNOWLEDGE_BUFFER: Vec = - [&FRAME_ACKNOWLEDGE_HEADER_BUFFER[..], &FRAME_ACKNOWLEDGE_BUFFER[..],].concat(); - pub static ref HEADER_WITH_WIRE_TO_SURFACE_1: ServerPdu = ServerPdu::WireToSurface1(WIRE_TO_SURFACE_1.clone()); - pub static ref HEADER_WITH_FRAME_ACKNOWLEDGE: ClientPdu = ClientPdu::FrameAcknowledge(FRAME_ACKNOWLEDGE.clone()); -} +pub static HEADER_WITH_WIRE_TO_SURFACE_1_BUFFER: LazyLock> = + LazyLock::new(|| [&WIRE_TO_SURFACE_1_HEADER_BUFFER[..], &WIRE_TO_SURFACE_1_BUFFER[..]].concat()); +pub static HEADER_WITH_FRAME_ACKNOWLEDGE_BUFFER: LazyLock> = + LazyLock::new(|| [&FRAME_ACKNOWLEDGE_HEADER_BUFFER[..], &FRAME_ACKNOWLEDGE_BUFFER[..]].concat()); +pub static HEADER_WITH_WIRE_TO_SURFACE_1: LazyLock = + LazyLock::new(|| ServerPdu::WireToSurface1(WIRE_TO_SURFACE_1.clone())); +pub static HEADER_WITH_FRAME_ACKNOWLEDGE: LazyLock = + LazyLock::new(|| ClientPdu::FrameAcknowledge(FRAME_ACKNOWLEDGE.clone())); diff --git a/crates/ironrdp-testsuite-core/src/graphics_messages.rs b/crates/ironrdp-testsuite-core/src/graphics_messages.rs index 395f59c5..ee0b4f0d 100644 --- a/crates/ironrdp-testsuite-core/src/graphics_messages.rs +++ b/crates/ironrdp-testsuite-core/src/graphics_messages.rs @@ -1,3 +1,5 @@ +use std::sync::LazyLock; + use ironrdp_pdu::gcc::{Monitor, MonitorFlags}; use ironrdp_pdu::geometry::InclusiveRectangle; use ironrdp_pdu::rdp::vc::dvc::gfx::{ @@ -8,7 +10,6 @@ use ironrdp_pdu::rdp::vc::dvc::gfx::{ PixelFormat, Point, QuantQuality, QueueDepth, ResetGraphicsPdu, SolidFillPdu, StartFramePdu, SurfaceToCachePdu, SurfaceToSurfacePdu, Timestamp, WireToSurface1Pdu, WireToSurface2Pdu, }; -use lazy_static::lazy_static; pub const WIRE_TO_SURFACE_1_BUFFER: [u8; 218] = [ 0x00, 0x00, 0x08, 0x00, 0x20, 0xa5, 0x03, 0xde, 0x02, 0xab, 0x03, 0xe7, 0x02, 0xc9, 0x00, 0x00, 0x00, 0x01, 0x0e, @@ -234,222 +235,221 @@ pub const AVC_444_MESSAGE_CORRECT_LEN: [u8; 88] = [ 0x1d, 0xe7, 0x97, 0xab, 0x80, 0x80, 0x80, ]; -lazy_static! { - pub static ref WIRE_TO_SURFACE_1: WireToSurface1Pdu = WireToSurface1Pdu { - surface_id: 0, - codec_id: Codec1Type::ClearCodec, - pixel_format: PixelFormat::XRgb, - destination_rectangle: InclusiveRectangle { - left: 933, - top: 734, - right: 939, - bottom: 743 - }, - bitmap_data: WIRE_TO_SURFACE_1_BUFFER[17..].to_vec(), - }; - pub static ref WIRE_TO_SURFACE_1_BITMAP_DATA: Vec = WIRE_TO_SURFACE_1_BUFFER[17..].to_vec(); - pub static ref WIRE_TO_SURFACE_2: WireToSurface2Pdu = WireToSurface2Pdu { - surface_id: 0, - codec_id: Codec2Type::RemoteFxProgressive, - codec_context_id: 4, - pixel_format: PixelFormat::XRgb, - bitmap_data: WIRE_TO_SURFACE_2_BUFFER[13..].to_vec(), - }; - pub static ref WIRE_TO_SURFACE_2_BITMAP_DATA: Vec = WIRE_TO_SURFACE_2_BUFFER[13..].to_vec(); - pub static ref DELETE_ENCODING_CONTEXT: DeleteEncodingContextPdu = DeleteEncodingContextPdu { - surface_id: 0, - codec_context_id: 1, - }; - pub static ref SOLID_FILL: SolidFillPdu = SolidFillPdu { - surface_id: 0, - fill_pixel: Color { - b: 0, - g: 0, - r: 0, - xa: 0, - }, - rectangles: vec![InclusiveRectangle { - left: 0, - top: 0, - right: 64, - bottom: 64 - }], - }; - pub static ref SURFACE_TO_SURFACE: SurfaceToSurfacePdu = SurfaceToSurfacePdu { - source_surface_id: 0, - destination_surface_id: 0, - source_rectangle: InclusiveRectangle { - left: 200, - top: 60, - right: 676, - bottom: 148 - }, - destination_points: vec![Point { x: 128, y: 60 }], - }; - pub static ref SURFACE_TO_CACHE: SurfaceToCachePdu = SurfaceToCachePdu { - surface_id: 0, - cache_key: 0x113D_86DA_A6A3_7FB7, - cache_slot: 14, - source_rectangle: InclusiveRectangle { - left: 640, - top: 0, - right: 704, - bottom: 64 - }, - }; - pub static ref CACHE_TO_SURFACE: CacheToSurfacePdu = CacheToSurfacePdu { - cache_slot: 2, - surface_id: 0, - destination_points: vec![Point { x: 768, y: 320 }], - }; - pub static ref CREATE_SURFACE: CreateSurfacePdu = CreateSurfacePdu { - surface_id: 0, - width: 1024, - height: 768, - pixel_format: PixelFormat::ARgb, - }; - pub static ref DELETE_SURFACE: DeleteSurfacePdu = DeleteSurfacePdu { surface_id: 0 }; - pub static ref RESET_GRAPHICS: ResetGraphicsPdu = ResetGraphicsPdu { - width: 1024, - height: 768, - monitors: vec![Monitor { - left: 0, - top: 0, - right: 1023, - bottom: 767, - flags: MonitorFlags::PRIMARY, - }], - }; - pub static ref MAP_SURFACE_TO_OUTPUT: MapSurfaceToOutputPdu = MapSurfaceToOutputPdu { - surface_id: 0, - output_origin_x: 1, - output_origin_y: 2, - }; - pub static ref EVICT_CACHE_ENTRY: EvictCacheEntryPdu = EvictCacheEntryPdu { cache_slot: 0 }; - pub static ref START_FRAME: StartFramePdu = StartFramePdu { - timestamp: Timestamp { - milliseconds: 247, - seconds: 58, - minutes: 27, - hours: 22, - }, - frame_id: 5 - }; - pub static ref END_FRAME: EndFramePdu = EndFramePdu { frame_id: 1 }; - pub static ref CAPABILITIES_CONFIRM: CapabilitiesConfirmPdu = CapabilitiesConfirmPdu(CapabilitySet::V10_5 { +pub static WIRE_TO_SURFACE_1: LazyLock = LazyLock::new(|| WireToSurface1Pdu { + surface_id: 0, + codec_id: Codec1Type::ClearCodec, + pixel_format: PixelFormat::XRgb, + destination_rectangle: InclusiveRectangle { + left: 933, + top: 734, + right: 939, + bottom: 743, + }, + bitmap_data: WIRE_TO_SURFACE_1_BUFFER[17..].to_vec(), +}); +pub static WIRE_TO_SURFACE_1_BITMAP_DATA: LazyLock> = LazyLock::new(|| WIRE_TO_SURFACE_1_BUFFER[17..].to_vec()); +pub static WIRE_TO_SURFACE_2: LazyLock = LazyLock::new(|| WireToSurface2Pdu { + surface_id: 0, + codec_id: Codec2Type::RemoteFxProgressive, + codec_context_id: 4, + pixel_format: PixelFormat::XRgb, + bitmap_data: WIRE_TO_SURFACE_2_BUFFER[13..].to_vec(), +}); +pub static WIRE_TO_SURFACE_2_BITMAP_DATA: LazyLock> = LazyLock::new(|| WIRE_TO_SURFACE_2_BUFFER[13..].to_vec()); +pub static DELETE_ENCODING_CONTEXT: LazyLock = LazyLock::new(|| DeleteEncodingContextPdu { + surface_id: 0, + codec_context_id: 1, +}); +pub static SOLID_FILL: LazyLock = LazyLock::new(|| SolidFillPdu { + surface_id: 0, + fill_pixel: Color { + b: 0, + g: 0, + r: 0, + xa: 0, + }, + rectangles: vec![InclusiveRectangle { + left: 0, + top: 0, + right: 64, + bottom: 64, + }], +}); +pub static SURFACE_TO_SURFACE: LazyLock = LazyLock::new(|| SurfaceToSurfacePdu { + source_surface_id: 0, + destination_surface_id: 0, + source_rectangle: InclusiveRectangle { + left: 200, + top: 60, + right: 676, + bottom: 148, + }, + destination_points: vec![Point { x: 128, y: 60 }], +}); +pub static SURFACE_TO_CACHE: LazyLock = LazyLock::new(|| SurfaceToCachePdu { + surface_id: 0, + cache_key: 0x113D_86DA_A6A3_7FB7, + cache_slot: 14, + source_rectangle: InclusiveRectangle { + left: 640, + top: 0, + right: 704, + bottom: 64, + }, +}); +pub static CACHE_TO_SURFACE: LazyLock = LazyLock::new(|| CacheToSurfacePdu { + cache_slot: 2, + surface_id: 0, + destination_points: vec![Point { x: 768, y: 320 }], +}); +pub static CREATE_SURFACE: LazyLock = LazyLock::new(|| CreateSurfacePdu { + surface_id: 0, + width: 1024, + height: 768, + pixel_format: PixelFormat::ARgb, +}); +pub static DELETE_SURFACE: LazyLock = LazyLock::new(|| DeleteSurfacePdu { surface_id: 0 }); +pub static RESET_GRAPHICS: LazyLock = LazyLock::new(|| ResetGraphicsPdu { + width: 1024, + height: 768, + monitors: vec![Monitor { + left: 0, + top: 0, + right: 1023, + bottom: 767, + flags: MonitorFlags::PRIMARY, + }], +}); +pub static MAP_SURFACE_TO_OUTPUT: LazyLock = LazyLock::new(|| MapSurfaceToOutputPdu { + surface_id: 0, + output_origin_x: 1, + output_origin_y: 2, +}); +pub static EVICT_CACHE_ENTRY: LazyLock = LazyLock::new(|| EvictCacheEntryPdu { cache_slot: 0 }); +pub static START_FRAME: LazyLock = LazyLock::new(|| StartFramePdu { + timestamp: Timestamp { + milliseconds: 247, + seconds: 58, + minutes: 27, + hours: 22, + }, + frame_id: 5, +}); +pub static END_FRAME: LazyLock = LazyLock::new(|| EndFramePdu { frame_id: 1 }); +pub static CAPABILITIES_CONFIRM: LazyLock = LazyLock::new(|| { + CapabilitiesConfirmPdu(CapabilitySet::V10_5 { flags: CapabilitiesV104Flags::AVC_DISABLED, - }); - pub static ref CAPABILITIES_ADVERTISE: CapabilitiesAdvertisePdu = CapabilitiesAdvertisePdu(vec![ + }) +}); +pub static CAPABILITIES_ADVERTISE: LazyLock = LazyLock::new(|| { + CapabilitiesAdvertisePdu(vec![ CapabilitySet::V8 { - flags: CapabilitiesV8Flags::THIN_CLIENT + flags: CapabilitiesV8Flags::THIN_CLIENT, }, CapabilitySet::V8_1 { - flags: CapabilitiesV81Flags::THIN_CLIENT + flags: CapabilitiesV81Flags::THIN_CLIENT, }, CapabilitySet::V10 { - flags: CapabilitiesV10Flags::AVC_DISABLED + flags: CapabilitiesV10Flags::AVC_DISABLED, }, CapabilitySet::V10_1, CapabilitySet::V10_2 { - flags: CapabilitiesV10Flags::AVC_DISABLED + flags: CapabilitiesV10Flags::AVC_DISABLED, }, CapabilitySet::V10_3 { - flags: CapabilitiesV103Flags::AVC_DISABLED + flags: CapabilitiesV103Flags::AVC_DISABLED, }, CapabilitySet::V10_4 { - flags: CapabilitiesV104Flags::AVC_DISABLED + flags: CapabilitiesV104Flags::AVC_DISABLED, }, CapabilitySet::V10_5 { - flags: CapabilitiesV104Flags::AVC_DISABLED + flags: CapabilitiesV104Flags::AVC_DISABLED, }, CapabilitySet::V10_6 { - flags: CapabilitiesV104Flags::AVC_DISABLED - } - ]); - pub static ref FRAME_ACKNOWLEDGE: FrameAcknowledgePdu = FrameAcknowledgePdu { - queue_depth: QueueDepth::Unavailable, - frame_id: 1, - total_frames_decoded: 1 - }; - pub static ref CACHE_IMPORT_REPLY: CacheImportReplyPdu = CacheImportReplyPdu { - cache_slots: vec![ - 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, - 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, - 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, - 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, - 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, - 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, - 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, - 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, - 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, - 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, - 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, - 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, 0x109, 0x10a, 0x10b, 0x10c, 0x10d, 0x10e, - 0x10f, 0x110, 0x111, 0x112, 0x113, 0x114, 0x115, 0x116, 0x117, 0x118, 0x119, 0x11a, 0x11b, 0x11c, 0x11d, - 0x11e, 0x11f, 0x120, 0x121, 0x122, 0x123, 0x124, 0x125, 0x126, 0x127, 0x128, 0x129, 0x12a, 0x12b, 0x12c, - 0x12d, 0x12e, 0x12f, 0x130, 0x131, 0x132, 0x133, 0x134, 0x135, 0x136, 0x137, 0x138, 0x139, 0x13a, 0x13b, - 0x13c, 0x13d, 0x13e, 0x13f, 0x140, 0x141, 0x142, 0x143, 0x144, 0x145, 0x146, 0x147, 0x148, 0x149, 0x14a, - 0x14b, 0x14c, 0x14d, 0x14e, 0x14f, 0x150, 0x151, 0x152, 0x153, 0x154, 0x155, 0x156, 0x157, 0x158, 0x159, - 0x15a, 0x15b, 0x15c, 0x15d, 0x15e, 0x15f, 0x160, 0x161, 0x162, 0x163, 0x164, 0x165, 0x166, 0x167, 0x168, - 0x169, 0x16a, 0x16b, 0x16c, 0x16d, 0x16e, 0x16f, 0x170, 0x171, 0x172, 0x173, 0x174, 0x175, 0x176, 0x177, - 0x178, 0x179, 0x17a, 0x17b, 0x17c, 0x17d, 0x17e, 0x17f, 0x180, 0x181, 0x182, 0x183, 0x184, 0x185, 0x186, - 0x187, 0x188, 0x189, 0x18a, 0x18b, 0x18c, 0x18d, 0x18e, 0x18f, 0x190, 0x191, 0x192, 0x193, 0x194, 0x195, - 0x196, 0x197, 0x198, 0x199, 0x19a, 0x19b, 0x19c, 0x19d, 0x19e, 0x19f, 0x1a0, 0x1a1, 0x1a2, 0x1a3, 0x1a4, - 0x1a5, 0x1a6, 0x1a7, 0x1a8, 0x1a9, 0x1aa, 0x1ab, 0x1ac, 0x1ad, 0x1ae, 0x1af, 0x1b0, 0x1b1, 0x1b2, 0x1b3, - 0x1b4, 0x1b5, 0x1b6, 0x1b7, 0x1b8, 0x1b9, 0x1ba, 0x1bb, 0x1bc, 0x1bd, 0x1be, 0x1bf, 0x1c0, 0x1c1, 0x1c2, - 0x1c3, 0x1c4, 0x1c5, 0x1c6, 0x1c7, 0x1c8, 0x1c9, 0x1ca, 0x1cb, 0x1cc, 0x1cd, 0x1ce, 0x1cf, 0x1d0, 0x1d1, - 0x1d2, 0x1d3, 0x1d4, 0x1d5, 0x1d6, 0x1d7, 0x1d8, 0x1d9, 0x1da, 0x1db, 0x1dc, 0x1dd, 0x1de, 0x1df, 0x1e0, - 0x1e1, 0x1e2, 0x1e3, 0x1e4, 0x1e5, 0x1e6, 0x1e7, 0x1e8, 0x1e9, 0x1ea, 0x1eb, 0x1ec, 0x1ed, 0x1ee, 0x1ef, - 0x1f0, 0x1f1, 0x1f2, 0x1f3, 0x1f4, 0x1f5, 0x1f6, 0x1f7, 0x1f8, 0x1f9, 0x1fa, 0x1fb, 0x1fc, 0x1fd, 0x1fe, - 0x1ff, 0x200, 0x201, 0x202, 0x203, 0x204, 0x205, 0x206, 0x207, 0x208, 0x209, 0x20a, 0x20b, 0x20c, 0x20d, - 0x20e, 0x20f, 0x210, 0x211, 0x212, 0x213, 0x214, 0x215, 0x216, 0x217, 0x218, 0x219, 0x21a, 0x21b, 0x21c, - 0x21d, 0x21e, 0x21f, 0x220, 0x221, 0x222, 0x223, 0x224, 0x225, 0x226, 0x227, 0x228, 0x229, 0x22a, 0x22b, - 0x22c, 0x22d, 0x22e, 0x22f, 0x230, 0x231, 0x232, 0x233, 0x234, 0x235, 0x236, 0x237, 0x238, 0x239, 0x23a, - 0x23b, 0x23c, 0x23d, 0x23e, 0x23f, 0x240, 0x241, 0x242, 0x243, 0x244, 0x245, 0x246, 0x247, 0x248, 0x249, - 0x24a, 0x24b, 0x24c, 0x24d, 0x24e, 0x24f, 0x250, 0x251, 0x252, 0x253, 0x254, 0x255, 0x256, 0x257, 0x258, - 0x259, 0x25a, 0x25b, 0x25c, 0x25d, 0x25e, 0x25f, 0x260, 0x261, 0x262, 0x263, 0x264, 0x265, 0x266, 0x267, - 0x268, 0x269, 0x26a, 0x26b, 0x26c, 0x26d, 0x26e, 0x26f, 0x270, 0x271, 0x272, 0x273, 0x274, 0x275, 0x276, - 0x277, 0x278, 0x279, 0x27a, 0x27b, 0x27c, 0x27d, 0x27e, 0x27f, 0x280, 0x281, 0x282, 0x283, 0x284, 0x285, - 0x286, 0x287, 0x288, 0x289, 0x28a, 0x28b, 0x28c, 0x28d, 0x28e, 0x28f, 0x290, 0x291, 0x292, 0x293, 0x294, - 0x295, 0x296, 0x297, 0x298, 0x299, 0x29a, 0x29b, 0x29c, 0x29d, 0x29e, 0x29f, 0x2a0, 0x2a1, 0x2a2, 0x2a3, - 0x2a4, 0x2a5, 0x2a6, 0x2a7, 0x2a8, 0x2a9, 0x2aa, 0x2ab, 0x2ac, 0x2ad, 0x2ae, 0x2af, 0x2b0, 0x2b1, 0x2b2, - 0x2b3, 0x2b4, 0x2b5, 0x2b6, 0x2b7, 0x2b8, 0x2b9, 0x2ba, 0x2bb, 0x2bc, 0x2bd, 0x2be, 0x2bf, 0x2c0, 0x2c1, - 0x2c2, 0x2c3, 0x2c4, 0x2c5, 0x2c6, 0x2c7, 0x2c8, 0x2c9, 0x2ca, 0x2cb, 0x2cc, 0x2cd, 0x2ce, 0x2cf, 0x2d0, - 0x2d1, 0x2d2, 0x2d3, 0x2d4, 0x2d5, 0x2d6, 0x2d7, 0x2d8, 0x2d9, 0x2da, 0x2db, 0x2dc, 0x2dd, 0x2de, 0x2df, - 0x2e0, 0x2e1, 0x2e2, 0x2e3, 0x2e4, 0x2e5, 0x2e6, 0x2e7, 0x2e8, 0x2e9, 0x2ea, 0x2eb, 0x2ec, 0x2ed, 0x2ee, - 0x2ef, 0x2f0, 0x2f1, 0x2f2, 0x2f3, 0x2f4, 0x2f5, 0x2f6, 0x2f7, 0x2f8, 0x2f9, 0x2fa, 0x2fb, 0x2fc, 0x2fd, - 0x2fe, 0x2ff, 0x300, 0x301, 0x302, 0x303, 0x304, 0x305, 0x306, 0x307, 0x308, 0x309, 0x30a, 0x30b, 0x30c, - 0x30d, 0x30e, 0x30f, 0x310, 0x311, 0x312, 0x313, 0x314, 0x315, 0x316, 0x317, 0x318, 0x319, 0x31a, 0x31b, - 0x31c, 0x31d, 0x31e, 0x31f, 0x320, 0x321, 0x322, 0x323, 0x324, 0x325, 0x326, 0x327, 0x328, 0x329, 0x32a, - 0x32b, 0x32c, 0x32d, 0x32e, 0x32f, 0x330, 0x331, 0x332, 0x333, 0x334, 0x335, 0x336, 0x337, 0x338, 0x339, - 0x33a, 0x33b, 0x33c, 0x33d, 0x33e, 0x33f, 0x340, 0x341, 0x342, 0x343, 0x344, 0x345, 0x346, 0x347, 0x348, - 0x349, 0x34a, 0x34b, 0x34c, 0x34d, 0x34e, 0x34f, 0x350, 0x351, 0x352, 0x353, 0x354, 0x355, 0x356, 0x357, - 0x358, 0x359, 0x35a, 0x35b, 0x35c, 0x35d, 0x35e, 0x35f, 0x360, 0x361, 0x362, 0x363, 0x364, 0x365, 0x366, - 0x367, 0x368, 0x369, 0x36a, 0x36b, 0x36c, 0x36d, 0x36e, 0x36f, 0x370, 0x371, 0x372, 0x373, 0x374, 0x375, - 0x376, 0x377, 0x378, 0x379, 0x37a, 0x37b, 0x37c, 0x37d, 0x37e, 0x37f, 0x380, 0x381, 0x382, 0x383, 0x384, - 0x385, 0x386, 0x387, 0x388, 0x389, 0x38a, 0x38b, 0x38c, 0x38d, 0x38e, 0x38f, 0x390, 0x391, 0x392, 0x393, - 0x394, 0x395, 0x396, 0x397, 0x398 - ] - }; - pub static ref AVC_444_BITMAP: Avc444BitmapStream<'static> = Avc444BitmapStream { - encoding: Encoding::CHROMA, - stream1: Avc420BitmapStream { - rectangles: vec![InclusiveRectangle { - left: 1792, - top: 1056, - right: 1808, - bottom: 1072, - }], - quant_qual_vals: vec![QuantQuality { - quantization_parameter: 22, - progressive: false, - quality: 100, - }], - data: &AVC_444_MESSAGE_CORRECT_LEN[18..] + flags: CapabilitiesV104Flags::AVC_DISABLED, }, - stream2: None - }; -} + ]) +}); +pub static FRAME_ACKNOWLEDGE: LazyLock = LazyLock::new(|| FrameAcknowledgePdu { + queue_depth: QueueDepth::Unavailable, + frame_id: 1, + total_frames_decoded: 1, +}); +pub static CACHE_IMPORT_REPLY: LazyLock = LazyLock::new(|| CacheImportReplyPdu { + cache_slots: vec![ + 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, + 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, + 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, + 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, + 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, + 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, + 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, + 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, + 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, + 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, + 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x100, + 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, 0x109, 0x10a, 0x10b, 0x10c, 0x10d, 0x10e, 0x10f, 0x110, + 0x111, 0x112, 0x113, 0x114, 0x115, 0x116, 0x117, 0x118, 0x119, 0x11a, 0x11b, 0x11c, 0x11d, 0x11e, 0x11f, 0x120, + 0x121, 0x122, 0x123, 0x124, 0x125, 0x126, 0x127, 0x128, 0x129, 0x12a, 0x12b, 0x12c, 0x12d, 0x12e, 0x12f, 0x130, + 0x131, 0x132, 0x133, 0x134, 0x135, 0x136, 0x137, 0x138, 0x139, 0x13a, 0x13b, 0x13c, 0x13d, 0x13e, 0x13f, 0x140, + 0x141, 0x142, 0x143, 0x144, 0x145, 0x146, 0x147, 0x148, 0x149, 0x14a, 0x14b, 0x14c, 0x14d, 0x14e, 0x14f, 0x150, + 0x151, 0x152, 0x153, 0x154, 0x155, 0x156, 0x157, 0x158, 0x159, 0x15a, 0x15b, 0x15c, 0x15d, 0x15e, 0x15f, 0x160, + 0x161, 0x162, 0x163, 0x164, 0x165, 0x166, 0x167, 0x168, 0x169, 0x16a, 0x16b, 0x16c, 0x16d, 0x16e, 0x16f, 0x170, + 0x171, 0x172, 0x173, 0x174, 0x175, 0x176, 0x177, 0x178, 0x179, 0x17a, 0x17b, 0x17c, 0x17d, 0x17e, 0x17f, 0x180, + 0x181, 0x182, 0x183, 0x184, 0x185, 0x186, 0x187, 0x188, 0x189, 0x18a, 0x18b, 0x18c, 0x18d, 0x18e, 0x18f, 0x190, + 0x191, 0x192, 0x193, 0x194, 0x195, 0x196, 0x197, 0x198, 0x199, 0x19a, 0x19b, 0x19c, 0x19d, 0x19e, 0x19f, 0x1a0, + 0x1a1, 0x1a2, 0x1a3, 0x1a4, 0x1a5, 0x1a6, 0x1a7, 0x1a8, 0x1a9, 0x1aa, 0x1ab, 0x1ac, 0x1ad, 0x1ae, 0x1af, 0x1b0, + 0x1b1, 0x1b2, 0x1b3, 0x1b4, 0x1b5, 0x1b6, 0x1b7, 0x1b8, 0x1b9, 0x1ba, 0x1bb, 0x1bc, 0x1bd, 0x1be, 0x1bf, 0x1c0, + 0x1c1, 0x1c2, 0x1c3, 0x1c4, 0x1c5, 0x1c6, 0x1c7, 0x1c8, 0x1c9, 0x1ca, 0x1cb, 0x1cc, 0x1cd, 0x1ce, 0x1cf, 0x1d0, + 0x1d1, 0x1d2, 0x1d3, 0x1d4, 0x1d5, 0x1d6, 0x1d7, 0x1d8, 0x1d9, 0x1da, 0x1db, 0x1dc, 0x1dd, 0x1de, 0x1df, 0x1e0, + 0x1e1, 0x1e2, 0x1e3, 0x1e4, 0x1e5, 0x1e6, 0x1e7, 0x1e8, 0x1e9, 0x1ea, 0x1eb, 0x1ec, 0x1ed, 0x1ee, 0x1ef, 0x1f0, + 0x1f1, 0x1f2, 0x1f3, 0x1f4, 0x1f5, 0x1f6, 0x1f7, 0x1f8, 0x1f9, 0x1fa, 0x1fb, 0x1fc, 0x1fd, 0x1fe, 0x1ff, 0x200, + 0x201, 0x202, 0x203, 0x204, 0x205, 0x206, 0x207, 0x208, 0x209, 0x20a, 0x20b, 0x20c, 0x20d, 0x20e, 0x20f, 0x210, + 0x211, 0x212, 0x213, 0x214, 0x215, 0x216, 0x217, 0x218, 0x219, 0x21a, 0x21b, 0x21c, 0x21d, 0x21e, 0x21f, 0x220, + 0x221, 0x222, 0x223, 0x224, 0x225, 0x226, 0x227, 0x228, 0x229, 0x22a, 0x22b, 0x22c, 0x22d, 0x22e, 0x22f, 0x230, + 0x231, 0x232, 0x233, 0x234, 0x235, 0x236, 0x237, 0x238, 0x239, 0x23a, 0x23b, 0x23c, 0x23d, 0x23e, 0x23f, 0x240, + 0x241, 0x242, 0x243, 0x244, 0x245, 0x246, 0x247, 0x248, 0x249, 0x24a, 0x24b, 0x24c, 0x24d, 0x24e, 0x24f, 0x250, + 0x251, 0x252, 0x253, 0x254, 0x255, 0x256, 0x257, 0x258, 0x259, 0x25a, 0x25b, 0x25c, 0x25d, 0x25e, 0x25f, 0x260, + 0x261, 0x262, 0x263, 0x264, 0x265, 0x266, 0x267, 0x268, 0x269, 0x26a, 0x26b, 0x26c, 0x26d, 0x26e, 0x26f, 0x270, + 0x271, 0x272, 0x273, 0x274, 0x275, 0x276, 0x277, 0x278, 0x279, 0x27a, 0x27b, 0x27c, 0x27d, 0x27e, 0x27f, 0x280, + 0x281, 0x282, 0x283, 0x284, 0x285, 0x286, 0x287, 0x288, 0x289, 0x28a, 0x28b, 0x28c, 0x28d, 0x28e, 0x28f, 0x290, + 0x291, 0x292, 0x293, 0x294, 0x295, 0x296, 0x297, 0x298, 0x299, 0x29a, 0x29b, 0x29c, 0x29d, 0x29e, 0x29f, 0x2a0, + 0x2a1, 0x2a2, 0x2a3, 0x2a4, 0x2a5, 0x2a6, 0x2a7, 0x2a8, 0x2a9, 0x2aa, 0x2ab, 0x2ac, 0x2ad, 0x2ae, 0x2af, 0x2b0, + 0x2b1, 0x2b2, 0x2b3, 0x2b4, 0x2b5, 0x2b6, 0x2b7, 0x2b8, 0x2b9, 0x2ba, 0x2bb, 0x2bc, 0x2bd, 0x2be, 0x2bf, 0x2c0, + 0x2c1, 0x2c2, 0x2c3, 0x2c4, 0x2c5, 0x2c6, 0x2c7, 0x2c8, 0x2c9, 0x2ca, 0x2cb, 0x2cc, 0x2cd, 0x2ce, 0x2cf, 0x2d0, + 0x2d1, 0x2d2, 0x2d3, 0x2d4, 0x2d5, 0x2d6, 0x2d7, 0x2d8, 0x2d9, 0x2da, 0x2db, 0x2dc, 0x2dd, 0x2de, 0x2df, 0x2e0, + 0x2e1, 0x2e2, 0x2e3, 0x2e4, 0x2e5, 0x2e6, 0x2e7, 0x2e8, 0x2e9, 0x2ea, 0x2eb, 0x2ec, 0x2ed, 0x2ee, 0x2ef, 0x2f0, + 0x2f1, 0x2f2, 0x2f3, 0x2f4, 0x2f5, 0x2f6, 0x2f7, 0x2f8, 0x2f9, 0x2fa, 0x2fb, 0x2fc, 0x2fd, 0x2fe, 0x2ff, 0x300, + 0x301, 0x302, 0x303, 0x304, 0x305, 0x306, 0x307, 0x308, 0x309, 0x30a, 0x30b, 0x30c, 0x30d, 0x30e, 0x30f, 0x310, + 0x311, 0x312, 0x313, 0x314, 0x315, 0x316, 0x317, 0x318, 0x319, 0x31a, 0x31b, 0x31c, 0x31d, 0x31e, 0x31f, 0x320, + 0x321, 0x322, 0x323, 0x324, 0x325, 0x326, 0x327, 0x328, 0x329, 0x32a, 0x32b, 0x32c, 0x32d, 0x32e, 0x32f, 0x330, + 0x331, 0x332, 0x333, 0x334, 0x335, 0x336, 0x337, 0x338, 0x339, 0x33a, 0x33b, 0x33c, 0x33d, 0x33e, 0x33f, 0x340, + 0x341, 0x342, 0x343, 0x344, 0x345, 0x346, 0x347, 0x348, 0x349, 0x34a, 0x34b, 0x34c, 0x34d, 0x34e, 0x34f, 0x350, + 0x351, 0x352, 0x353, 0x354, 0x355, 0x356, 0x357, 0x358, 0x359, 0x35a, 0x35b, 0x35c, 0x35d, 0x35e, 0x35f, 0x360, + 0x361, 0x362, 0x363, 0x364, 0x365, 0x366, 0x367, 0x368, 0x369, 0x36a, 0x36b, 0x36c, 0x36d, 0x36e, 0x36f, 0x370, + 0x371, 0x372, 0x373, 0x374, 0x375, 0x376, 0x377, 0x378, 0x379, 0x37a, 0x37b, 0x37c, 0x37d, 0x37e, 0x37f, 0x380, + 0x381, 0x382, 0x383, 0x384, 0x385, 0x386, 0x387, 0x388, 0x389, 0x38a, 0x38b, 0x38c, 0x38d, 0x38e, 0x38f, 0x390, + 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, + ], +}); +pub static AVC_444_BITMAP: LazyLock> = LazyLock::new(|| Avc444BitmapStream { + encoding: Encoding::CHROMA, + stream1: Avc420BitmapStream { + rectangles: vec![InclusiveRectangle { + left: 1792, + top: 1056, + right: 1808, + bottom: 1072, + }], + quant_qual_vals: vec![QuantQuality { + quantization_parameter: 22, + progressive: false, + quality: 100, + }], + data: &AVC_444_MESSAGE_CORRECT_LEN[18..], + }, + stream2: None, +}); diff --git a/crates/ironrdp-testsuite-core/src/mcs.rs b/crates/ironrdp-testsuite-core/src/mcs.rs index 0a23f38a..5836945d 100644 --- a/crates/ironrdp-testsuite-core/src/mcs.rs +++ b/crates/ironrdp-testsuite-core/src/mcs.rs @@ -1,4 +1,5 @@ use std::borrow::Cow; +use std::sync::LazyLock; use array_concat::{concat_arrays, concat_arrays_size}; use ironrdp_pdu::mcs::{ @@ -6,7 +7,6 @@ use ironrdp_pdu::mcs::{ DisconnectProviderUltimatum, DisconnectReason, DomainParameters, ErectDomainPdu, OwnedSendDataIndication, OwnedSendDataRequest, SendDataIndication, SendDataRequest, }; -use lazy_static::lazy_static; use crate::conference_create::{ CONFERENCE_CREATE_REQUEST, CONFERENCE_CREATE_REQUEST_BUFFER, CONFERENCE_CREATE_RESPONSE, @@ -105,55 +105,53 @@ pub const CONNECT_RESPONSE_BUFFER: [u8; concat_arrays_size!( CONFERENCE_CREATE_RESPONSE_BUFFER )] = concat_arrays!(CONNECT_RESPONSE_PREFIX_BUFFER, CONFERENCE_CREATE_RESPONSE_BUFFER); -lazy_static! { - pub static ref CONNECT_INITIAL: ConnectInitial = ConnectInitial { - calling_domain_selector: vec![0x01], - called_domain_selector: vec![0x01], - upward_flag: true, - target_parameters: DomainParameters { - max_channel_ids: 34, - max_user_ids: 2, - max_token_ids: 0, - num_priorities: 1, - min_throughput: 0, - max_height: 1, - max_mcs_pdu_size: 65535, - protocol_version: 2, - }, - min_parameters: DomainParameters { - max_channel_ids: 1, - max_user_ids: 1, - max_token_ids: 1, - num_priorities: 1, - min_throughput: 0, - max_height: 1, - max_mcs_pdu_size: 1056, - protocol_version: 2, - }, - max_parameters: DomainParameters { - max_channel_ids: 65535, - max_user_ids: 64535, - max_token_ids: 65535, - num_priorities: 1, - min_throughput: 0, - max_height: 1, - max_mcs_pdu_size: 65535, - protocol_version: 2, - }, - conference_create_request: CONFERENCE_CREATE_REQUEST.clone(), - }; - pub static ref CONNECT_RESPONSE: ConnectResponse = ConnectResponse { - called_connect_id: 0, - domain_parameters: DomainParameters { - max_channel_ids: 34, - max_user_ids: 3, - max_token_ids: 0, - num_priorities: 1, - min_throughput: 0, - max_height: 1, - max_mcs_pdu_size: 65528, - protocol_version: 2, - }, - conference_create_response: CONFERENCE_CREATE_RESPONSE.clone(), - }; -} +pub static CONNECT_INITIAL: LazyLock = LazyLock::new(|| ConnectInitial { + calling_domain_selector: vec![0x01], + called_domain_selector: vec![0x01], + upward_flag: true, + target_parameters: DomainParameters { + max_channel_ids: 34, + max_user_ids: 2, + max_token_ids: 0, + num_priorities: 1, + min_throughput: 0, + max_height: 1, + max_mcs_pdu_size: 65535, + protocol_version: 2, + }, + min_parameters: DomainParameters { + max_channel_ids: 1, + max_user_ids: 1, + max_token_ids: 1, + num_priorities: 1, + min_throughput: 0, + max_height: 1, + max_mcs_pdu_size: 1056, + protocol_version: 2, + }, + max_parameters: DomainParameters { + max_channel_ids: 65535, + max_user_ids: 64535, + max_token_ids: 65535, + num_priorities: 1, + min_throughput: 0, + max_height: 1, + max_mcs_pdu_size: 65535, + protocol_version: 2, + }, + conference_create_request: CONFERENCE_CREATE_REQUEST.clone(), +}); +pub static CONNECT_RESPONSE: LazyLock = LazyLock::new(|| ConnectResponse { + called_connect_id: 0, + domain_parameters: DomainParameters { + max_channel_ids: 34, + max_user_ids: 3, + max_token_ids: 0, + num_priorities: 1, + min_throughput: 0, + max_height: 1, + max_mcs_pdu_size: 65528, + protocol_version: 2, + }, + conference_create_response: CONFERENCE_CREATE_RESPONSE.clone(), +}); diff --git a/crates/ironrdp-testsuite-core/src/monitor_data.rs b/crates/ironrdp-testsuite-core/src/monitor_data.rs index b39aa67e..1bd15bc0 100644 --- a/crates/ironrdp-testsuite-core/src/monitor_data.rs +++ b/crates/ironrdp-testsuite-core/src/monitor_data.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_pdu::gcc::{ClientMonitorData, Monitor, MonitorFlags}; -use lazy_static::lazy_static; pub const MONITOR_DATA_WITHOUT_MONITORS_BUFFER: [u8; 8] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; @@ -9,24 +10,23 @@ pub const MONITOR_DATA_WITH_MONITORS_BUFFER: [u8; 48] = [ 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; -lazy_static! { - pub static ref MONITOR_DATA_WITHOUT_MONITORS: ClientMonitorData = ClientMonitorData { monitors: Vec::new() }; - pub static ref MONITOR_DATA_WITH_MONITORS: ClientMonitorData = ClientMonitorData { - monitors: vec![ - Monitor { - left: 0, - top: 0, - right: 1919, - bottom: 1079, - flags: MonitorFlags::PRIMARY, - }, - Monitor { - left: -1280, - top: 0, - right: -1, - bottom: 1023, - flags: MonitorFlags::empty(), - } - ] - }; -} +pub static MONITOR_DATA_WITHOUT_MONITORS: LazyLock = + LazyLock::new(|| ClientMonitorData { monitors: Vec::new() }); +pub static MONITOR_DATA_WITH_MONITORS: LazyLock = LazyLock::new(|| ClientMonitorData { + monitors: vec![ + Monitor { + left: 0, + top: 0, + right: 1919, + bottom: 1079, + flags: MonitorFlags::PRIMARY, + }, + Monitor { + left: -1280, + top: 0, + right: -1, + bottom: 1023, + flags: MonitorFlags::empty(), + }, + ], +}); diff --git a/crates/ironrdp-testsuite-core/src/monitor_extended_data.rs b/crates/ironrdp-testsuite-core/src/monitor_extended_data.rs index 31a09e34..089f9679 100644 --- a/crates/ironrdp-testsuite-core/src/monitor_extended_data.rs +++ b/crates/ironrdp-testsuite-core/src/monitor_extended_data.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_pdu::gcc::{ClientMonitorExtendedData, ExtendedMonitorInfo, MonitorOrientation}; -use lazy_static::lazy_static; pub const MONITOR_DATA_WITHOUT_MONITORS_BUFFER: [u8; 12] = [0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; @@ -10,11 +11,12 @@ pub const MONITOR_DATA_WITH_MONITORS_BUFFER: [u8; 52] = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; -lazy_static! { - pub static ref MONITOR_DATA_WITHOUT_MONITORS: ClientMonitorExtendedData = ClientMonitorExtendedData { - extended_monitors_info: Vec::new() - }; - pub static ref MONITOR_DATA_WITH_MONITORS: ClientMonitorExtendedData = ClientMonitorExtendedData { +pub static MONITOR_DATA_WITHOUT_MONITORS: LazyLock = + LazyLock::new(|| ClientMonitorExtendedData { + extended_monitors_info: Vec::new(), + }); +pub static MONITOR_DATA_WITH_MONITORS: LazyLock = + LazyLock::new(|| ClientMonitorExtendedData { extended_monitors_info: vec![ ExtendedMonitorInfo { physical_width: 0, @@ -29,7 +31,6 @@ lazy_static! { orientation: MonitorOrientation::Landscape, desktop_scale_factor: 0, device_scale_factor: 0, - } - ] - }; -} + }, + ], + }); diff --git a/crates/ironrdp-testsuite-core/src/multi_transport_channel_data.rs b/crates/ironrdp-testsuite-core/src/multi_transport_channel_data.rs index fc485140..c950d891 100644 --- a/crates/ironrdp-testsuite-core/src/multi_transport_channel_data.rs +++ b/crates/ironrdp-testsuite-core/src/multi_transport_channel_data.rs @@ -1,12 +1,12 @@ +use std::sync::LazyLock; + use ironrdp_pdu::gcc::{MultiTransportChannelData, MultiTransportFlags}; -use lazy_static::lazy_static; pub const SERVER_GCC_MULTI_TRANSPORT_CHANNEL_BLOCK_BUFFER: [u8; 4] = [0x01, 0x03, 0x00, 0x00]; -lazy_static! { - pub static ref SERVER_GCC_MULTI_TRANSPORT_CHANNEL_BLOCK: MultiTransportChannelData = MultiTransportChannelData { +pub static SERVER_GCC_MULTI_TRANSPORT_CHANNEL_BLOCK: LazyLock = + LazyLock::new(|| MultiTransportChannelData { flags: MultiTransportFlags::TRANSPORT_TYPE_UDP_FECR | MultiTransportFlags::TRANSPORT_TYPE_UDP_PREFERRED | MultiTransportFlags::SOFT_SYNC_TCP_TO_UDP, - }; -} + }); diff --git a/crates/ironrdp-testsuite-core/src/network_data.rs b/crates/ironrdp-testsuite-core/src/network_data.rs index 3c94472c..ad8b4196 100644 --- a/crates/ironrdp-testsuite-core/src/network_data.rs +++ b/crates/ironrdp-testsuite-core/src/network_data.rs @@ -1,5 +1,6 @@ +use std::sync::LazyLock; + use ironrdp_pdu::gcc::{ChannelDef, ChannelName, ChannelOptions, ClientNetworkData, ServerNetworkData}; -use lazy_static::lazy_static; pub const CLIENT_NETWORK_DATA_WITH_CHANNELS_BUFFER: [u8; 40] = [ 0x03, 0x00, 0x00, 0x00, // channels count @@ -29,33 +30,32 @@ pub const SERVER_NETWORK_DATA_WITHOUT_CHANNELS_ID_BUFFER: [u8; 4] = [ 0x00, 0x00, // channels count ]; -lazy_static! { - pub static ref CLIENT_NETWORK_DATA_WITH_CHANNELS: ClientNetworkData = ClientNetworkData { - channels: vec![ - ChannelDef { - name: ChannelName::from_utf8("rdpdr").unwrap(), - options: ChannelOptions::INITIALIZED | ChannelOptions::COMPRESS_RDP, - }, - ChannelDef { - name: ChannelName::from_utf8("cliprdr").unwrap(), - options: ChannelOptions::INITIALIZED - | ChannelOptions::COMPRESS_RDP - | ChannelOptions::ENCRYPT_RDP - | ChannelOptions::SHOW_PROTOCOL, - }, - ChannelDef { - name: ChannelName::from_utf8("rdpsnd").unwrap(), - options: ChannelOptions::INITIALIZED | ChannelOptions::ENCRYPT_RDP, - }, - ], - }; - pub static ref SERVER_NETWORK_DATA_WITH_CHANNELS_ID: ServerNetworkData = ServerNetworkData { - io_channel: 1003, - channel_ids: vec![1004, 1005, 1006], - }; - pub static ref CLIENT_NETWORK_DATA_WITHOUT_CHANNELS: ClientNetworkData = ClientNetworkData { channels: Vec::new() }; - pub static ref SERVER_NETWORK_DATA_WITHOUT_CHANNELS_ID: ServerNetworkData = ServerNetworkData { - io_channel: 1003, - channel_ids: Vec::new(), - }; -} +pub static CLIENT_NETWORK_DATA_WITH_CHANNELS: LazyLock = LazyLock::new(|| ClientNetworkData { + channels: vec![ + ChannelDef { + name: ChannelName::from_utf8("rdpdr").unwrap(), + options: ChannelOptions::INITIALIZED | ChannelOptions::COMPRESS_RDP, + }, + ChannelDef { + name: ChannelName::from_utf8("cliprdr").unwrap(), + options: ChannelOptions::INITIALIZED + | ChannelOptions::COMPRESS_RDP + | ChannelOptions::ENCRYPT_RDP + | ChannelOptions::SHOW_PROTOCOL, + }, + ChannelDef { + name: ChannelName::from_utf8("rdpsnd").unwrap(), + options: ChannelOptions::INITIALIZED | ChannelOptions::ENCRYPT_RDP, + }, + ], +}); +pub static SERVER_NETWORK_DATA_WITH_CHANNELS_ID: LazyLock = LazyLock::new(|| ServerNetworkData { + io_channel: 1003, + channel_ids: vec![1004, 1005, 1006], +}); +pub static CLIENT_NETWORK_DATA_WITHOUT_CHANNELS: LazyLock = + LazyLock::new(|| ClientNetworkData { channels: Vec::new() }); +pub static SERVER_NETWORK_DATA_WITHOUT_CHANNELS_ID: LazyLock = LazyLock::new(|| ServerNetworkData { + io_channel: 1003, + channel_ids: Vec::new(), +}); diff --git a/crates/ironrdp-testsuite-core/src/rdp.rs b/crates/ironrdp-testsuite-core/src/rdp.rs index 488b2c90..51d788bd 100644 --- a/crates/ironrdp-testsuite-core/src/rdp.rs +++ b/crates/ironrdp-testsuite-core/src/rdp.rs @@ -1,3 +1,5 @@ +use std::sync::LazyLock; + use array_concat::{concat_arrays, concat_arrays_size}; use ironrdp_pdu::gcc; use ironrdp_pdu::rdp::finalization_messages::{ @@ -12,7 +14,6 @@ use ironrdp_pdu::rdp::server_license::{ PreambleType, PreambleVersion, }; use ironrdp_pdu::rdp::{client_info, ClientInfoPdu}; -use lazy_static::lazy_static; use crate::capsets::{ CLIENT_DEMAND_ACTIVE, CLIENT_DEMAND_ACTIVE_BUFFER, SERVER_DEMAND_ACTIVE, SERVER_DEMAND_ACTIVE_BUFFER, @@ -158,145 +159,143 @@ pub const SERVER_LICENSE_BUFFER: [u8; 20] = [ 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, ]; -lazy_static! { - pub static ref CLIENT_INFO_PDU: ClientInfoPdu = ClientInfoPdu { - security_header: BasicSecurityHeader { - flags: BasicSecurityHeaderFlags::INFO_PKT, - }, - client_info: CLIENT_INFO_UNICODE.clone(), - }; - pub static ref SERVER_LICENSE_PDU: LicensePdu = { - let mut pdu = LicensingErrorMessage { - license_header: LicenseHeader { - security_header: BasicSecurityHeader { - flags: BasicSecurityHeaderFlags::LICENSE_PKT, - }, - preamble_message_type: PreambleType::ErrorAlert, - preamble_flags: PreambleFlags::empty(), - preamble_version: PreambleVersion::V3, - preamble_message_size: 0, +pub static CLIENT_INFO_PDU: LazyLock = LazyLock::new(|| ClientInfoPdu { + security_header: BasicSecurityHeader { + flags: BasicSecurityHeaderFlags::INFO_PKT, + }, + client_info: CLIENT_INFO_UNICODE.clone(), +}); +pub static SERVER_LICENSE_PDU: LazyLock = LazyLock::new(|| { + let mut pdu = LicensingErrorMessage { + license_header: LicenseHeader { + security_header: BasicSecurityHeader { + flags: BasicSecurityHeaderFlags::LICENSE_PKT, }, - error_code: LicenseErrorCode::StatusValidClient, - state_transition: LicensingStateTransition::NoTransition, - error_info: Vec::new(), - }; - pdu.license_header.preamble_message_size = pdu.size() as u16; - pdu.into() + preamble_message_type: PreambleType::ErrorAlert, + preamble_flags: PreambleFlags::empty(), + preamble_version: PreambleVersion::V3, + preamble_message_size: 0, + }, + error_code: LicenseErrorCode::StatusValidClient, + state_transition: LicensingStateTransition::NoTransition, + error_info: Vec::new(), }; - pub static ref SERVER_DEMAND_ACTIVE_PDU: ShareControlHeader = ShareControlHeader { - share_control_pdu: ShareControlPdu::ServerDemandActive(SERVER_DEMAND_ACTIVE.clone()), - pdu_source: 1002, - share_id: 66_538, - }; - pub static ref CLIENT_DEMAND_ACTIVE_PDU: ShareControlHeader = ShareControlHeader { - share_control_pdu: ShareControlPdu::ClientConfirmActive(CLIENT_DEMAND_ACTIVE.clone()), - pdu_source: 1007, - share_id: 66_538, - }; - pub static ref CLIENT_SYNCHRONIZE: ShareControlHeader = ShareControlHeader { - share_control_pdu: ShareControlPdu::Data(ShareDataHeader { - share_data_pdu: ShareDataPdu::Synchronize(SynchronizePdu { target_user_id: 0x03ea }), - stream_priority: StreamPriority::Low, - compression_flags: CompressionFlags::empty(), - compression_type: client_info::CompressionType::K8, + pdu.license_header.preamble_message_size = pdu.size() as u16; + pdu.into() +}); +pub static SERVER_DEMAND_ACTIVE_PDU: LazyLock = LazyLock::new(|| ShareControlHeader { + share_control_pdu: ShareControlPdu::ServerDemandActive(SERVER_DEMAND_ACTIVE.clone()), + pdu_source: 1002, + share_id: 66_538, +}); +pub static CLIENT_DEMAND_ACTIVE_PDU: LazyLock = LazyLock::new(|| ShareControlHeader { + share_control_pdu: ShareControlPdu::ClientConfirmActive(CLIENT_DEMAND_ACTIVE.clone()), + pdu_source: 1007, + share_id: 66_538, +}); +pub static CLIENT_SYNCHRONIZE: LazyLock = LazyLock::new(|| ShareControlHeader { + share_control_pdu: ShareControlPdu::Data(ShareDataHeader { + share_data_pdu: ShareDataPdu::Synchronize(SynchronizePdu { target_user_id: 0x03ea }), + stream_priority: StreamPriority::Low, + compression_flags: CompressionFlags::empty(), + compression_type: client_info::CompressionType::K8, + }), + pdu_source: 1007, + share_id: 66_538, +}); +pub static CONTROL_COOPERATE: LazyLock = LazyLock::new(|| ShareControlHeader { + share_control_pdu: ShareControlPdu::Data(ShareDataHeader { + share_data_pdu: ShareDataPdu::Control(ControlPdu { + action: ControlAction::Cooperate, + grant_id: 0, + control_id: 0, }), - pdu_source: 1007, - share_id: 66_538, - }; - pub static ref CONTROL_COOPERATE: ShareControlHeader = ShareControlHeader { - share_control_pdu: ShareControlPdu::Data(ShareDataHeader { - share_data_pdu: ShareDataPdu::Control(ControlPdu { - action: ControlAction::Cooperate, - grant_id: 0, - control_id: 0, - }), - stream_priority: StreamPriority::Low, - compression_flags: CompressionFlags::empty(), - compression_type: client_info::CompressionType::K8, + stream_priority: StreamPriority::Low, + compression_flags: CompressionFlags::empty(), + compression_type: client_info::CompressionType::K8, + }), + pdu_source: 1007, + share_id: 66_538, +}); +pub static CONTROL_REQUEST_CONTROL: LazyLock = LazyLock::new(|| ShareControlHeader { + share_control_pdu: ShareControlPdu::Data(ShareDataHeader { + share_data_pdu: ShareDataPdu::Control(ControlPdu { + action: ControlAction::RequestControl, + grant_id: 0, + control_id: 0, }), - pdu_source: 1007, - share_id: 66_538, - }; - pub static ref CONTROL_REQUEST_CONTROL: ShareControlHeader = ShareControlHeader { - share_control_pdu: ShareControlPdu::Data(ShareDataHeader { - share_data_pdu: ShareDataPdu::Control(ControlPdu { - action: ControlAction::RequestControl, - grant_id: 0, - control_id: 0, - }), - stream_priority: StreamPriority::Low, - compression_flags: CompressionFlags::empty(), - compression_type: client_info::CompressionType::K8, + stream_priority: StreamPriority::Low, + compression_flags: CompressionFlags::empty(), + compression_type: client_info::CompressionType::K8, + }), + pdu_source: 1007, + share_id: 66_538, +}); +pub static SERVER_GRANTED_CONTROL: LazyLock = LazyLock::new(|| ShareControlHeader { + share_control_pdu: ShareControlPdu::Data(ShareDataHeader { + share_data_pdu: ShareDataPdu::Control(ControlPdu { + action: ControlAction::GrantedControl, + grant_id: 1007, + control_id: 1002, }), - pdu_source: 1007, - share_id: 66_538, - }; - pub static ref SERVER_GRANTED_CONTROL: ShareControlHeader = ShareControlHeader { - share_control_pdu: ShareControlPdu::Data(ShareDataHeader { - share_data_pdu: ShareDataPdu::Control(ControlPdu { - action: ControlAction::GrantedControl, - grant_id: 1007, - control_id: 1002, - }), - stream_priority: StreamPriority::Medium, - compression_flags: CompressionFlags::empty(), - compression_type: client_info::CompressionType::K8, + stream_priority: StreamPriority::Medium, + compression_flags: CompressionFlags::empty(), + compression_type: client_info::CompressionType::K8, + }), + pdu_source: 1002, + share_id: 66_538, +}); +pub static CLIENT_FONT_LIST: LazyLock = LazyLock::new(|| ShareControlHeader { + share_control_pdu: ShareControlPdu::Data(ShareDataHeader { + share_data_pdu: ShareDataPdu::FontList(FontPdu { + number: 0, + total_number: 0, + flags: SequenceFlags::FIRST | SequenceFlags::LAST, + entry_size: 50, }), - pdu_source: 1002, - share_id: 66_538, - }; - pub static ref CLIENT_FONT_LIST: ShareControlHeader = ShareControlHeader { - share_control_pdu: ShareControlPdu::Data(ShareDataHeader { - share_data_pdu: ShareDataPdu::FontList(FontPdu { - number: 0, - total_number: 0, - flags: SequenceFlags::FIRST | SequenceFlags::LAST, - entry_size: 50, - }), - stream_priority: StreamPriority::Low, - compression_flags: CompressionFlags::empty(), - compression_type: client_info::CompressionType::K8, + stream_priority: StreamPriority::Low, + compression_flags: CompressionFlags::empty(), + compression_type: client_info::CompressionType::K8, + }), + pdu_source: 1007, + share_id: 66_538, +}); +pub static SERVER_FONT_MAP: LazyLock = LazyLock::new(|| ShareControlHeader { + share_control_pdu: ShareControlPdu::Data(ShareDataHeader { + share_data_pdu: ShareDataPdu::FontMap(FontPdu { + number: 0, + total_number: 0, + flags: SequenceFlags::FIRST | SequenceFlags::LAST, + entry_size: 4, }), - pdu_source: 1007, - share_id: 66_538, - }; - pub static ref SERVER_FONT_MAP: ShareControlHeader = ShareControlHeader { - share_control_pdu: ShareControlPdu::Data(ShareDataHeader { - share_data_pdu: ShareDataPdu::FontMap(FontPdu { - number: 0, - total_number: 0, - flags: SequenceFlags::FIRST | SequenceFlags::LAST, - entry_size: 4, - }), - stream_priority: StreamPriority::Medium, - compression_flags: CompressionFlags::empty(), - compression_type: client_info::CompressionType::K8, + stream_priority: StreamPriority::Medium, + compression_flags: CompressionFlags::empty(), + compression_type: client_info::CompressionType::K8, + }), + pdu_source: 1002, + share_id: 66_538, +}); +pub static MONITOR_LAYOUT_PDU: LazyLock = LazyLock::new(|| ShareControlHeader { + share_control_pdu: ShareControlPdu::Data(ShareDataHeader { + share_data_pdu: ShareDataPdu::MonitorLayout(MonitorLayoutPdu { + monitors: crate::monitor_data::MONITOR_DATA_WITH_MONITORS.monitors.clone(), }), - pdu_source: 1002, - share_id: 66_538, - }; - pub static ref MONITOR_LAYOUT_PDU: ShareControlHeader = ShareControlHeader { - share_control_pdu: ShareControlPdu::Data(ShareDataHeader { - share_data_pdu: ShareDataPdu::MonitorLayout(MonitorLayoutPdu { - monitors: crate::monitor_data::MONITOR_DATA_WITH_MONITORS.monitors.clone(), - }), - stream_priority: StreamPriority::Low, - compression_flags: CompressionFlags::empty(), - compression_type: client_info::CompressionType::K8, - }), - pdu_source: 1007, - share_id: 66_538, - }; - pub static ref MONITOR_LAYOUT_PDU_BUFFER: Vec = { - let mut buffer = MONITOR_LAYOUT_HEADERS_BUFFER.to_vec(); - buffer.extend( - MONITOR_DATA_WITH_MONITORS_BUFFER - .to_vec() - .split_off(gcc::MONITOR_FLAGS_SIZE), - ); - buffer - }; -} + stream_priority: StreamPriority::Low, + compression_flags: CompressionFlags::empty(), + compression_type: client_info::CompressionType::K8, + }), + pdu_source: 1007, + share_id: 66_538, +}); +pub static MONITOR_LAYOUT_PDU_BUFFER: LazyLock> = LazyLock::new(|| { + let mut buffer = MONITOR_LAYOUT_HEADERS_BUFFER.to_vec(); + buffer.extend( + MONITOR_DATA_WITH_MONITORS_BUFFER + .to_vec() + .split_off(gcc::MONITOR_FLAGS_SIZE), + ); + buffer +}); pub const CLIENT_INFO_PDU_BUFFER: [u8; concat_arrays_size!( CLIENT_INFO_PDU_SECURITY_HEADER_BUFFER, diff --git a/crates/ironrdp-testsuite-core/src/security_data.rs b/crates/ironrdp-testsuite-core/src/security_data.rs index 96a1a2f4..336c7364 100644 --- a/crates/ironrdp-testsuite-core/src/security_data.rs +++ b/crates/ironrdp-testsuite-core/src/security_data.rs @@ -1,6 +1,7 @@ +use std::sync::LazyLock; + use array_concat::concat_arrays; use ironrdp_pdu::gcc::{ClientSecurityData, EncryptionLevel, EncryptionMethod, ServerSecurityData}; -use lazy_static::lazy_static; pub const CLIENT_SECURITY_DATA_BUFFER: [u8; 8] = [ 0x1b, 0x00, 0x00, 0x00, // encryption methods @@ -35,34 +36,34 @@ pub const SERVER_CERT_BUFFER: [u8; 184] = [ 0x6c, 0xd6, 0x76, 0x84, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; -lazy_static! { - pub static ref CLIENT_SECURITY_DATA: ClientSecurityData = ClientSecurityData { - encryption_methods: EncryptionMethod::BIT_40 - | EncryptionMethod::BIT_128 - | EncryptionMethod::BIT_56 - | EncryptionMethod::FIPS, - ext_encryption_methods: 0, - }; - pub static ref SERVER_SECURITY_DATA_WITHOUT_OPTIONAL_FIELDS: ServerSecurityData = ServerSecurityData { +pub static CLIENT_SECURITY_DATA: LazyLock = LazyLock::new(|| ClientSecurityData { + encryption_methods: EncryptionMethod::BIT_40 + | EncryptionMethod::BIT_128 + | EncryptionMethod::BIT_56 + | EncryptionMethod::FIPS, + ext_encryption_methods: 0, +}); +pub static SERVER_SECURITY_DATA_WITHOUT_OPTIONAL_FIELDS: LazyLock = + LazyLock::new(|| ServerSecurityData { encryption_method: EncryptionMethod::empty(), encryption_level: EncryptionLevel::None, server_random: None, server_cert: Vec::new(), - }; - pub static ref SERVER_SECURITY_DATA_WITH_OPTIONAL_FIELDS: ServerSecurityData = ServerSecurityData { + }); +pub static SERVER_SECURITY_DATA_WITH_OPTIONAL_FIELDS: LazyLock = + LazyLock::new(|| ServerSecurityData { encryption_method: EncryptionMethod::BIT_128, encryption_level: EncryptionLevel::ClientCompatible, server_random: Some(SERVER_RANDOM_BUFFER), server_cert: SERVER_CERT_BUFFER.to_vec(), - }; - pub static ref SERVER_SECURITY_DATA_WITH_MISMATCH_OF_REQUIRED_AND_OPTIONAL_FIELDS: ServerSecurityData = - ServerSecurityData { - encryption_method: EncryptionMethod::empty(), - encryption_level: EncryptionLevel::None, - server_random: Some(SERVER_RANDOM_BUFFER), - server_cert: SERVER_CERT_BUFFER.to_vec(), - }; -} + }); +pub static SERVER_SECURITY_DATA_WITH_MISMATCH_OF_REQUIRED_AND_OPTIONAL_FIELDS: LazyLock = + LazyLock::new(|| ServerSecurityData { + encryption_method: EncryptionMethod::empty(), + encryption_level: EncryptionLevel::None, + server_random: Some(SERVER_RANDOM_BUFFER), + server_cert: SERVER_CERT_BUFFER.to_vec(), + }); pub const SERVER_SECURITY_DATA_WITH_OPTIONAL_FIELDS_BUFFER: [u8; 232] = concat_arrays!( SERVER_SECURITY_DATA_WITH_OPTIONAL_FIELDS_PREFIX_BUFFER, diff --git a/crates/ironrdp-testsuite-core/tests/pdu/input.rs b/crates/ironrdp-testsuite-core/tests/pdu/input.rs index 63e26682..7b31672c 100644 --- a/crates/ironrdp-testsuite-core/tests/pdu/input.rs +++ b/crates/ironrdp-testsuite-core/tests/pdu/input.rs @@ -1,3 +1,5 @@ +use std::sync::LazyLock; + use ironrdp_core::{decode_cursor, encode_vec, ReadCursor}; use ironrdp_pdu::input::fast_path::{FastPathInput, FastPathInputEvent}; use ironrdp_pdu::input::mouse::PointerFlags; @@ -9,46 +11,47 @@ const FASTPATH_INPUT_MESSAGE: [u8; 44] = [ 0x0, 0x28, 0x4, ]; -lazy_static::lazy_static! { - pub static ref FASTPATH_INPUT: FastPathInput = FastPathInput::new(vec![ +static FASTPATH_INPUT: LazyLock = LazyLock::new(|| { + FastPathInput::new(vec![ FastPathInputEvent::MouseEvent(MousePdu { flags: PointerFlags::DOWN | PointerFlags::LEFT_BUTTON, number_of_wheel_rotation_units: 0, x_position: 26, - y_position: 1062 + y_position: 1062, }), FastPathInputEvent::MouseEvent(MousePdu { flags: PointerFlags::MOVE, number_of_wheel_rotation_units: 0, x_position: 27, - y_position: 1062 + y_position: 1062, }), FastPathInputEvent::MouseEvent(MousePdu { flags: PointerFlags::LEFT_BUTTON, number_of_wheel_rotation_units: 0, x_position: 27, - y_position: 1062 + y_position: 1062, }), FastPathInputEvent::MouseEvent(MousePdu { flags: PointerFlags::MOVE, number_of_wheel_rotation_units: 0, x_position: 26, - y_position: 1063 + y_position: 1063, }), FastPathInputEvent::MouseEvent(MousePdu { flags: PointerFlags::MOVE, number_of_wheel_rotation_units: 0, x_position: 25, - y_position: 1063 + y_position: 1063, }), FastPathInputEvent::MouseEvent(MousePdu { flags: PointerFlags::MOVE, number_of_wheel_rotation_units: 0, x_position: 25, - y_position: 1064 - }) - ]).expect("can't panic"); -} + y_position: 1064, + }), + ]) + .expect("can't panic") +}); #[test] fn from_buffer_correctly_parses_fastpath_input_message() { diff --git a/crates/ironrdp-testsuite-core/tests/pdu/rfx.rs b/crates/ironrdp-testsuite-core/tests/pdu/rfx.rs index 9a8f968b..c6221933 100644 --- a/crates/ironrdp-testsuite-core/tests/pdu/rfx.rs +++ b/crates/ironrdp-testsuite-core/tests/pdu/rfx.rs @@ -1,3 +1,5 @@ +use std::sync::LazyLock; + use ironrdp_pdu::codecs::rfx::*; use ironrdp_pdu::decode; use ironrdp_testsuite_core::encode_decode_test; @@ -252,12 +254,14 @@ const FRAME_BEGIN_PDU: Block<'_> = Block::CodecChannel(CodecChannel::FrameBegin( const FRAME_END_PDU: Block<'_> = Block::CodecChannel(CodecChannel::FrameEnd(FrameEndPdu)); -lazy_static::lazy_static! { - static ref CHANNELS_PDU: Block<'static> = Block::Channels(ChannelsPdu(vec![ +static CHANNELS_PDU: LazyLock> = LazyLock::new(|| { + Block::Channels(ChannelsPdu(vec![ RfxChannel { width: 64, height: 64 }, - RfxChannel { width: 32, height: 32 } - ])); - static ref REGION_PDU: Block<'static> = Block::CodecChannel(CodecChannel::Region(RegionPdu { + RfxChannel { width: 32, height: 32 }, + ])) +}); +static REGION_PDU: LazyLock> = LazyLock::new(|| { + Block::CodecChannel(CodecChannel::Region(RegionPdu { rectangles: vec![ RfxRectangle { x: 0, @@ -271,9 +275,11 @@ lazy_static::lazy_static! { width: 0xff, height: 0xff, }, - ] - })); - static ref TILESET_PDU: Block<'static> = Block::CodecChannel(CodecChannel::TileSet(TileSetPdu { + ], + })) +}); +static TILESET_PDU: LazyLock> = LazyLock::new(|| { + Block::CodecChannel(CodecChannel::TileSet(TileSetPdu { entropy_algorithm: EntropyAlgorithm::Rlgr3, quants: vec![ Quant { @@ -316,8 +322,8 @@ lazy_static::lazy_static! { cr_data: &TILE2_CR_DATA, }, ], - })); -} + })) +}); #[test] fn from_buffer_for_block_header_returns_error_on_zero_data_length() { diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index a6f3ad32..9d7b1889 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -371,7 +371,6 @@ dependencies = [ "byteorder", "ironrdp-core", "ironrdp-pdu", - "lazy_static", "num-derive", "num-traits", "yuv", @@ -441,12 +440,6 @@ dependencies = [ "libc", ] -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - [[package]] name = "libc" version = "0.2.176" From bbdecc2aa98e4419f4586bf0b470dcf31f722741 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 05:04:36 -0400 Subject: [PATCH 22/51] build(deps): bump clap from 4.5.49 to 4.5.50 in the patch group across 1 directory (#1023) --- Cargo.lock | 8 ++++---- fuzz/Cargo.lock | 15 +++------------ 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 38985bc0..ea165ee6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -708,9 +708,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.49" +version = "4.5.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4512b90fa68d3a9932cea5184017c5d200f5921df706d45e853537dea51508f" +checksum = "0c2cfd7bf8a6017ddaa4e32ffe7403d547790db06bd171c1c53926faab501623" dependencies = [ "clap_builder", "clap_derive", @@ -718,9 +718,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.49" +version = "4.5.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0025e98baa12e766c67ba13ff4695a887a1eba19569aad00a472546795bd6730" +checksum = "0a4c05b9e80c5ccd3a7ef080ad7b6ba7d6fc00a985b8b157197075677c82c7a0" dependencies = [ "anstream", "anstyle", diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 9d7b1889..c91b71f6 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -275,14 +275,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi", + "wasip2", ] [[package]] @@ -775,15 +775,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "wasi" -version = "0.14.7+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" -dependencies = [ - "wasip2", -] - [[package]] name = "wasip2" version = "1.0.1+wasi-0.2.4" From abc391c134129c9eee44d93817dc584dcb2f2005 Mon Sep 17 00:00:00 2001 From: Alex Yusiuk <55661041+RRRadicalEdward@users.noreply.github.com> Date: Thu, 23 Oct 2025 17:58:41 +0300 Subject: [PATCH 23/51] refactor: add `partial_pub_fields` clippy style and readability lint (#976) --- Cargo.toml | 1 + crates/ironrdp-acceptor/src/connection.rs | 2 +- crates/ironrdp-acceptor/src/finalization.rs | 6 +- crates/ironrdp-client/src/rdp.rs | 2 +- .../src/pdu/format_data/metafile.rs | 6 +- crates/ironrdp-connector/src/connection.rs | 6 +- .../src/connection_activation.rs | 9 ++- .../src/connection_finalization.rs | 4 +- crates/ironrdp-connector/src/lib.rs | 2 +- crates/ironrdp-dvc/src/client.rs | 8 +-- crates/ironrdp-dvc/src/complete_data.rs | 16 ++--- crates/ironrdp-dvc/src/pdu.rs | 68 ++++++++++++++++--- crates/ironrdp-dvc/src/server.rs | 12 ++-- crates/ironrdp-error/src/lib.rs | 8 ++- crates/ironrdp-mstsgu/src/lib.rs | 4 +- crates/ironrdp-mstsgu/src/proto.rs | 16 +++-- .../src/basic_output/fast_path/mod.rs | 4 ++ .../src/rdp/capability_sets/order/mod.rs | 8 +-- crates/ironrdp-rdpsnd-native/examples/cpal.rs | 2 +- crates/ironrdp-rdpsnd-native/src/cpal.rs | 6 +- .../ironrdp-server/src/encoder/fast_path.rs | 4 ++ crates/ironrdp-session/src/fast_path.rs | 2 +- crates/ironrdp-session/src/lib.rs | 2 +- .../tests/clipboard/mod.rs | 2 +- crates/ironrdp-testsuite-extra/tests/mod.rs | 2 +- crates/ironrdp-web/src/error.rs | 2 +- crates/ironrdp-web/src/session.rs | 2 +- ffi/src/connector/activation.rs | 12 ++-- ffi/src/error.rs | 2 +- 29 files changed, 150 insertions(+), 70 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 77859972..058d516d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,6 +127,7 @@ empty_enum_variants_with_brackets = "warn" deref_by_slicing = "warn" multiple_inherent_impl = "warn" map_with_unused_argument_over_ranges = "warn" +partial_pub_fields = "warn" trait_duplication_in_bounds = "warn" type_repetition_in_bounds = "warn" checked_conversions = "warn" diff --git a/crates/ironrdp-acceptor/src/connection.rs b/crates/ironrdp-acceptor/src/connection.rs index 492caab3..6643b941 100644 --- a/crates/ironrdp-acceptor/src/connection.rs +++ b/crates/ironrdp-acceptor/src/connection.rs @@ -713,7 +713,7 @@ impl Sequence for Acceptor { AcceptorState::Accepted { channels, client_capabilities, - input_events: finalization.input_events, + input_events: finalization.into_input_events(), } } else { AcceptorState::ConnectionFinalization { diff --git a/crates/ironrdp-acceptor/src/finalization.rs b/crates/ironrdp-acceptor/src/finalization.rs index 2560e530..9961e87c 100644 --- a/crates/ironrdp-acceptor/src/finalization.rs +++ b/crates/ironrdp-acceptor/src/finalization.rs @@ -12,7 +12,7 @@ pub struct FinalizationSequence { user_channel_id: u16, io_channel_id: u16, - pub input_events: Vec>, + input_events: Vec>, } #[derive(Default, Debug)] @@ -190,6 +190,10 @@ impl FinalizationSequence { } } + pub fn into_input_events(self) -> Vec> { + self.input_events + } + pub fn is_done(&self) -> bool { self.state.is_terminal() } diff --git a/crates/ironrdp-client/src/rdp.rs b/crates/ironrdp-client/src/rdp.rs index 394050dd..53ecb034 100644 --- a/crates/ironrdp-client/src/rdp.rs +++ b/crates/ironrdp-client/src/rdp.rs @@ -660,7 +660,7 @@ async fn active_session( desktop_size, enable_server_pointer, pointer_software_rendering, - } = connection_activation.state + } = connection_activation.connection_activation_state() { debug!(?desktop_size, "Deactivation-Reactivation Sequence completed"); // Update image size with the new desktop size. diff --git a/crates/ironrdp-cliprdr/src/pdu/format_data/metafile.rs b/crates/ironrdp-cliprdr/src/pdu/format_data/metafile.rs index 4569687b..433f2e81 100644 --- a/crates/ironrdp-cliprdr/src/pdu/format_data/metafile.rs +++ b/crates/ironrdp-cliprdr/src/pdu/format_data/metafile.rs @@ -42,7 +42,7 @@ pub struct PackedMetafile<'a> { pub x_ext: u32, pub y_ext: u32, /// The variable sized contents of the metafile as specified in [MS-WMF] section 2 - data: Cow<'a, [u8]>, + pub data: Cow<'a, [u8]>, } impl PackedMetafile<'_> { @@ -62,10 +62,6 @@ impl PackedMetafile<'_> { data: data.into(), } } - - pub fn data(&self) -> &[u8] { - &self.data - } } impl Encode for PackedMetafile<'_> { diff --git a/crates/ironrdp-connector/src/connection.rs b/crates/ironrdp-connector/src/connection.rs index 1ddd09ee..639cf93a 100644 --- a/crates/ironrdp-connector/src/connection.rs +++ b/crates/ironrdp-connector/src/connection.rs @@ -553,7 +553,7 @@ impl Sequence for ClientConnector { mut connection_activation, } => { let written = connection_activation.step(input, output)?; - match connection_activation.state { + match connection_activation.connection_activation_state() { ConnectionActivationState::ConnectionFinalization { .. } => ( written, ClientConnectorState::ConnectionFinalization { connection_activation }, @@ -570,10 +570,10 @@ impl Sequence for ClientConnector { } => { let written = connection_activation.step(input, output)?; - let next_state = if !connection_activation.state.is_terminal() { + let next_state = if !connection_activation.connection_activation_state().is_terminal() { ClientConnectorState::ConnectionFinalization { connection_activation } } else { - match connection_activation.state { + match connection_activation.connection_activation_state() { ConnectionActivationState::Finalized { io_channel_id, user_channel_id, diff --git a/crates/ironrdp-connector/src/connection_activation.rs b/crates/ironrdp-connector/src/connection_activation.rs index 999cea24..110ba29d 100644 --- a/crates/ironrdp-connector/src/connection_activation.rs +++ b/crates/ironrdp-connector/src/connection_activation.rs @@ -22,7 +22,7 @@ use crate::{ /// [Server Deactivate All PDU]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/8a29971a-df3c-48da-add2-8ed9a05edc89 #[derive(Debug, Clone)] pub struct ConnectionActivationSequence { - pub state: ConnectionActivationState, + state: ConnectionActivationState, config: Config, } @@ -37,6 +37,11 @@ impl ConnectionActivationSequence { } } + /// Returns the current state as a district type, rather than `&dyn State` provided by [`Self::state`]. + pub fn connection_activation_state(&self) -> ConnectionActivationState { + self.state + } + #[must_use] pub fn reset_clone(&self) -> Self { self.clone().reset() @@ -215,7 +220,7 @@ impl Sequence for ConnectionActivationSequence { } } -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Copy, Clone)] pub enum ConnectionActivationState { #[default] Consumed, diff --git a/crates/ironrdp-connector/src/connection_finalization.rs b/crates/ironrdp-connector/src/connection_finalization.rs index 6267822c..cb626d99 100644 --- a/crates/ironrdp-connector/src/connection_finalization.rs +++ b/crates/ironrdp-connector/src/connection_finalization.rs @@ -9,7 +9,7 @@ use tracing::{debug, warn}; use crate::{general_err, legacy, reason_err, ConnectorResult, Sequence, State, Written}; -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Copy, Clone)] #[non_exhaustive] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] pub enum ConnectionFinalizationState { @@ -48,7 +48,7 @@ impl State for ConnectionFinalizationState { } } -#[derive(Debug, Clone)] +#[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] pub struct ConnectionFinalizationSequence { pub state: ConnectionFinalizationState, diff --git a/crates/ironrdp-connector/src/lib.rs b/crates/ironrdp-connector/src/lib.rs index 971e149a..1e1a7402 100644 --- a/crates/ironrdp-connector/src/lib.rs +++ b/crates/ironrdp-connector/src/lib.rs @@ -406,7 +406,7 @@ pub trait ConnectorResultExt { impl ConnectorResultExt for ConnectorResult { fn with_context(self, context: &'static str) -> Self { self.map_err(|mut e| { - e.context = context; + e.set_context(context); e }) } diff --git a/crates/ironrdp-dvc/src/client.rs b/crates/ironrdp-dvc/src/client.rs index 532b1000..ed383d53 100644 --- a/crates/ironrdp-dvc/src/client.rs +++ b/crates/ironrdp-dvc/src/client.rs @@ -116,8 +116,8 @@ impl SvcProcessor for DrdynvcClient { } DrdynvcServerPdu::Create(create_request) => { debug!("Got DVC Create Request PDU: {create_request:?}"); - let channel_name = create_request.channel_name; - let channel_id = create_request.channel_id; + let channel_id = create_request.channel_id(); + let channel_name = create_request.into_channel_name(); if !self.cap_handshake_done { debug!( @@ -156,9 +156,9 @@ impl SvcProcessor for DrdynvcClient { } DrdynvcServerPdu::Close(close_request) => { debug!("Got DVC Close Request PDU: {close_request:?}"); - self.dynamic_channels.remove_by_channel_id(close_request.channel_id); + self.dynamic_channels.remove_by_channel_id(close_request.channel_id()); - let close_response = DrdynvcClientPdu::Close(ClosePdu::new(close_request.channel_id)); + let close_response = DrdynvcClientPdu::Close(ClosePdu::new(close_request.channel_id())); debug!("Send DVC Close Response PDU: {close_response:?}"); responses.push(SvcMessage::from(close_response)); diff --git a/crates/ironrdp-dvc/src/complete_data.rs b/crates/ironrdp-dvc/src/complete_data.rs index acb6d919..d910aa4d 100644 --- a/crates/ironrdp-dvc/src/complete_data.rs +++ b/crates/ironrdp-dvc/src/complete_data.rs @@ -28,7 +28,7 @@ impl CompleteData { } fn process_data_first_pdu(&mut self, data_first: DataFirstPdu) -> DecodeResult>> { - let total_data_size: DecodeResult<_> = cast_length!("DataFirstPdu::length", data_first.length); + let total_data_size: DecodeResult<_> = cast_length!("DataFirstPdu::length", data_first.length()); let total_data_size = total_data_size?; if self.total_size != 0 || !self.data.is_empty() { error!("Incomplete DVC message, it will be skipped"); @@ -36,11 +36,11 @@ impl CompleteData { self.data.clear(); } - if total_data_size == data_first.data.len() { - Ok(Some(data_first.data)) + if total_data_size == data_first.data().len() { + Ok(Some(data_first.into_data())) } else { self.total_size = total_data_size; - self.data = data_first.data; + self.data = data_first.into_data(); Ok(None) } @@ -49,22 +49,22 @@ impl CompleteData { fn process_data_pdu(&mut self, mut data: DataPdu) -> DecodeResult>> { if self.total_size == 0 && self.data.is_empty() { // message is not fragmented - return Ok(Some(data.data)); + return Ok(Some(data.into_data())); } // The message is fragmented and needs to be reassembled. - match self.data.len().checked_add(data.data.len()) { + match self.data.len().checked_add(data.data().len()) { Some(actual_data_length) => { match actual_data_length.cmp(&(self.total_size)) { cmp::Ordering::Less => { // this is one of the fragmented messages, just append it - self.data.append(&mut data.data); + self.data.append(data.data_mut()); Ok(None) } cmp::Ordering::Equal => { // this is the last fragmented message, need to return the whole reassembled message self.total_size = 0; - self.data.append(&mut data.data); + self.data.append(data.data_mut()); Ok(Some(self.data.drain(..).collect())) } cmp::Ordering::Greater => { diff --git a/crates/ironrdp-dvc/src/pdu.rs b/crates/ironrdp-dvc/src/pdu.rs index 0b081b6d..ef81987f 100644 --- a/crates/ironrdp-dvc/src/pdu.rs +++ b/crates/ironrdp-dvc/src/pdu.rs @@ -282,12 +282,12 @@ impl From for String { #[derive(Debug, PartialEq)] pub struct DataFirstPdu { header: Header, - pub channel_id: DynamicChannelId, + channel_id: DynamicChannelId, /// Length is the *total* length of the data to be sent, including the length /// of the data that will be sent by subsequent DVC_DATA PDUs. - pub length: u32, + length: u32, /// Data is just the data to be sent in this PDU. - pub data: Vec, + data: Vec, } impl DataFirstPdu { @@ -322,6 +322,18 @@ impl DataFirstPdu { } } + pub fn length(&self) -> u32 { + self.length + } + + pub fn data(&self) -> &[u8] { + &self.data + } + + pub fn into_data(self) -> Vec { + self.data + } + fn decode(header: Header, src: &mut ReadCursor<'_>) -> DecodeResult { let fixed_part_size = checked_sum(&[header.cb_id.size_of_val(), header.sp.size_of_val()])?; ensure_size!(in: src, size: fixed_part_size); @@ -434,8 +446,8 @@ impl From for u8 { #[derive(Debug, PartialEq)] pub struct DataPdu { header: Header, - pub channel_id: DynamicChannelId, - pub data: Vec, + channel_id: DynamicChannelId, + data: Vec, } impl DataPdu { @@ -447,6 +459,18 @@ impl DataPdu { } } + pub fn data(&self) -> &[u8] { + &self.data + } + + pub fn into_data(self) -> Vec { + self.data + } + + pub fn data_mut(&mut self) -> &mut Vec { + &mut self.data + } + fn decode(header: Header, src: &mut ReadCursor<'_>) -> DecodeResult { ensure_size!(in: src, size: header.cb_id.size_of_val()); let channel_id = header.cb_id.decode_val(src)?; @@ -485,8 +509,8 @@ impl DataPdu { #[derive(Debug, PartialEq)] pub struct CreateResponsePdu { header: Header, - pub channel_id: DynamicChannelId, - pub creation_status: CreationStatus, + channel_id: DynamicChannelId, + creation_status: CreationStatus, } impl CreateResponsePdu { @@ -498,6 +522,14 @@ impl CreateResponsePdu { } } + pub fn channel_id(&self) -> DynamicChannelId { + self.channel_id + } + + pub fn creation_status(&self) -> CreationStatus { + self.creation_status + } + fn name() -> &'static str { "DYNVC_CREATE_RSP" } @@ -564,7 +596,7 @@ impl From for u32 { #[derive(Debug, PartialEq)] pub struct ClosePdu { header: Header, - pub channel_id: DynamicChannelId, + channel_id: DynamicChannelId, } impl ClosePdu { @@ -583,6 +615,10 @@ impl ClosePdu { } } + pub fn channel_id(&self) -> DynamicChannelId { + self.channel_id + } + fn decode(header: Header, src: &mut ReadCursor<'_>) -> DecodeResult { ensure_size!(in: src, size: Self::headerless_size(&header)); let channel_id = header.cb_id.decode_val(src)?; @@ -798,8 +834,8 @@ impl CapabilitiesRequestPdu { #[derive(Debug, PartialEq)] pub struct CreateRequestPdu { header: Header, - pub channel_id: DynamicChannelId, - pub channel_name: String, + channel_id: DynamicChannelId, + channel_name: String, } impl CreateRequestPdu { @@ -811,6 +847,18 @@ impl CreateRequestPdu { } } + pub fn channel_id(&self) -> DynamicChannelId { + self.channel_id + } + + pub fn channel_name(&self) -> &str { + &self.channel_name + } + + pub fn into_channel_name(self) -> String { + self.channel_name + } + fn decode(header: Header, src: &mut ReadCursor<'_>) -> DecodeResult { ensure_size!(in: src, size: Self::headerless_fixed_part_size(&header)); let channel_id = header.cb_id.decode_val(src)?; diff --git a/crates/ironrdp-dvc/src/server.rs b/crates/ironrdp-dvc/src/server.rs index 09c1700d..1811f240 100644 --- a/crates/ironrdp-dvc/src/server.rs +++ b/crates/ironrdp-dvc/src/server.rs @@ -139,22 +139,24 @@ impl SvcProcessor for DrdynvcServer { } DrdynvcClientPdu::Create(create_resp) => { debug!("Got DVC Create Response PDU: {create_resp:?}"); - let id = create_resp.channel_id; + let id = create_resp.channel_id(); let c = self.channel_by_id(id).map_err(|e| decode_err!(e))?; if c.state != ChannelState::Creation { return Err(pdu_other_err!("invalid channel state")); } - if create_resp.creation_status != CreationStatus::OK { - c.state = ChannelState::CreationFailed(create_resp.creation_status.into()); + if create_resp.creation_status() != CreationStatus::OK { + c.state = ChannelState::CreationFailed(create_resp.creation_status().into()); return Ok(resp); } c.state = ChannelState::Opened; - let msg = c.processor.start(create_resp.channel_id)?; + let msg = c.processor.start(create_resp.channel_id())?; resp.extend(encode_dvc_messages(id, msg, ChannelFlags::SHOW_PROTOCOL).map_err(|e| encode_err!(e))?); } DrdynvcClientPdu::Close(close_resp) => { debug!("Got DVC Close Response PDU: {close_resp:?}"); - let c = self.channel_by_id(close_resp.channel_id).map_err(|e| decode_err!(e))?; + let c = self + .channel_by_id(close_resp.channel_id()) + .map_err(|e| decode_err!(e))?; if c.state != ChannelState::Opened { return Err(pdu_other_err!("invalid channel state")); } diff --git a/crates/ironrdp-error/src/lib.rs b/crates/ironrdp-error/src/lib.rs index 4687bcff..82316e74 100644 --- a/crates/ironrdp-error/src/lib.rs +++ b/crates/ironrdp-error/src/lib.rs @@ -23,8 +23,8 @@ impl Source for T where T: fmt::Display + fmt::Debug + Send + Sync + 'static #[derive(Debug)] pub struct Error { - pub context: &'static str, - pub kind: Kind, + context: &'static str, + kind: Kind, #[cfg(feature = "std")] source: Option>, #[cfg(all(not(feature = "std"), feature = "alloc"))] @@ -80,6 +80,10 @@ impl Error { &self.kind } + pub fn set_context(&mut self, context: &'static str) { + self.context = context; + } + pub fn report(&self) -> ErrorReport<'_, Kind> { ErrorReport(self) } diff --git a/crates/ironrdp-mstsgu/src/lib.rs b/crates/ironrdp-mstsgu/src/lib.rs index 8ed962ad..d6d2e63b 100644 --- a/crates/ironrdp-mstsgu/src/lib.rs +++ b/crates/ironrdp-mstsgu/src/lib.rs @@ -350,7 +350,7 @@ impl GwConn { let resp: TunnelAuthRespPkt = TunnelAuthRespPkt::decode(&mut cur).map_err(|_| Error::new("TunnelAuth", GwErrorKind::Decode))?; - if resp.error_code != 0 { + if resp.error_code() != 0 { return Err(Error::new("TunnelAuth", GwErrorKind::Connect)); } Ok(()) @@ -369,7 +369,7 @@ impl GwConn { let mut cur: ReadCursor<'_> = ReadCursor::new(&bytes); let resp: ChannelResp = ChannelResp::decode(&mut cur).map_err(|_| Error::new("ChannelResp", GwErrorKind::Decode))?; - if resp.error_code != 0 { + if resp.error_code() != 0 { return Err(Error::new("ChannelCreate", GwErrorKind::Connect)); } assert!(cur.eof()); diff --git a/crates/ironrdp-mstsgu/src/proto.rs b/crates/ironrdp-mstsgu/src/proto.rs index 494c7eff..0c7a0a9b 100644 --- a/crates/ironrdp-mstsgu/src/proto.rs +++ b/crates/ironrdp-mstsgu/src/proto.rs @@ -66,7 +66,7 @@ impl TryFrom for PktTy { #[derive(Default, Debug)] pub(crate) struct PktHdr { pub ty: PktTy, - _reserved: u16, + pub _reserved: u16, pub length: u32, } @@ -183,7 +183,7 @@ impl Decode<'_> for HandshakeRespPkt { pub(crate) struct TunnelReqPkt { pub caps: u32, pub fields_present: u16, - pub(crate) _reserved: u16, + pub _reserved: u16, } impl Encode for TunnelReqPkt { @@ -384,13 +384,17 @@ impl Encode for TunnelAuthPkt { /// 2.2.10.16 HTTP_TUNNEL_AUTH_RESPONSE Structure #[derive(Debug)] pub(crate) struct TunnelAuthRespPkt { - pub error_code: u32, + error_code: u32, _fields_present: u16, _reserved: u16, } impl TunnelAuthRespPkt { const FIXED_PART_SIZE: usize = 4 /* error_code */ + 2 /* fields_present */ + 2 /* _reserved */; + + pub(crate) fn error_code(&self) -> u32 { + self.error_code + } } impl Decode<'_> for TunnelAuthRespPkt { @@ -455,7 +459,7 @@ impl Encode for ChannelPkt { /// 2.2.10.4 HTTP_CHANNEL_RESPONSE #[derive(Default, Debug)] pub(crate) struct ChannelResp { - pub error_code: u32, + error_code: u32, fields_present: u16, _reserved: u16, @@ -467,6 +471,10 @@ pub(crate) struct ChannelResp { impl ChannelResp { const FIXED_PART_SIZE: usize = 4 /* error_code */ + 2 /* fields_present */ + 2 /* _reserved */; + + pub(crate) fn error_code(&self) -> u32 { + self.error_code + } } impl Decode<'_> for ChannelResp { diff --git a/crates/ironrdp-pdu/src/basic_output/fast_path/mod.rs b/crates/ironrdp-pdu/src/basic_output/fast_path/mod.rs index 940086ed..c0c11aac 100644 --- a/crates/ironrdp-pdu/src/basic_output/fast_path/mod.rs +++ b/crates/ironrdp-pdu/src/basic_output/fast_path/mod.rs @@ -19,6 +19,10 @@ use crate::rdp::headers::{CompressionFlags, SHARE_DATA_HEADER_COMPRESSION_MASK}; /// Implements the Fast-Path RDP message header PDU. /// TS_FP_UPDATE_PDU +#[expect( + clippy::partial_pub_fields, + reason = "this structure is used in the match expression in the integration tests" +)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct FastPathHeader { pub flags: EncryptionFlags, diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/order/mod.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/order/mod.rs index 2b3f8680..70d823cb 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/order/mod.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/order/mod.rs @@ -66,11 +66,11 @@ bitflags! { #[derive(Debug, PartialEq, Eq, Clone)] pub struct Order { - pub order_flags: OrderFlags, + order_flags: OrderFlags, order_support: [u8; SUPPORT_ARRAY_LEN], - pub order_support_ex_flags: OrderSupportExFlags, - pub desktop_save_size: u32, - pub text_ansi_code_page: u16, + order_support_ex_flags: OrderSupportExFlags, + desktop_save_size: u32, + text_ansi_code_page: u16, } impl Order { diff --git a/crates/ironrdp-rdpsnd-native/examples/cpal.rs b/crates/ironrdp-rdpsnd-native/examples/cpal.rs index 9486abbe..2372e9d0 100644 --- a/crates/ironrdp-rdpsnd-native/examples/cpal.rs +++ b/crates/ironrdp-rdpsnd-native/examples/cpal.rs @@ -54,7 +54,7 @@ fn main() -> anyhow::Result<()> { } }); - stream.stream.play()?; + stream.stream().play()?; thread::sleep(Duration::from_secs(3)); let _ = producer.join(); diff --git a/crates/ironrdp-rdpsnd-native/src/cpal.rs b/crates/ironrdp-rdpsnd-native/src/cpal.rs index bd5bf96e..00faa8db 100644 --- a/crates/ironrdp-rdpsnd-native/src/cpal.rs +++ b/crates/ironrdp-rdpsnd-native/src/cpal.rs @@ -134,7 +134,7 @@ impl RdpsndClientHandler for RdpsndBackend { #[doc(hidden)] pub struct DecodeStream { _dec_thread: Option>, - pub stream: Stream, + stream: Stream, } impl DecodeStream { @@ -222,6 +222,10 @@ impl DecodeStream { stream, }) } + + pub fn stream(&self) -> &Stream { + &self.stream + } } struct RxBuffer { diff --git a/crates/ironrdp-server/src/encoder/fast_path.rs b/crates/ironrdp-server/src/encoder/fast_path.rs index 90ed98e4..1b4da818 100644 --- a/crates/ironrdp-server/src/encoder/fast_path.rs +++ b/crates/ironrdp-server/src/encoder/fast_path.rs @@ -13,6 +13,10 @@ const FASTPATH_HEADER_SIZE: usize = 6; reason = "Unfortunately, expect attribute doesn't work when above or after visibility::make attribute" )] #[allow(unreachable_pub)] +#[expect( + clippy::partial_pub_fields, + reason = "public field is not a part of the public API and is used by benchmarks" +)] #[cfg_attr(feature = "__bench", visibility::make(pub))] pub(crate) struct UpdateFragmenter { code: UpdateCode, diff --git a/crates/ironrdp-session/src/fast_path.rs b/crates/ironrdp-session/src/fast_path.rs index 06d07cc3..9826fbf9 100644 --- a/crates/ironrdp-session/src/fast_path.rs +++ b/crates/ironrdp-session/src/fast_path.rs @@ -298,7 +298,7 @@ impl Processor { // FIXME: This seems to be a way of special-handling the error case in FastPathUpdate::decode_cursor_with_code // to ignore the unsupported update PDUs, but this is a fragile logic and the rationale behind it is not // obvious. - if let DecodeErrorKind::InvalidField { field, reason } = e.kind { + if let DecodeErrorKind::InvalidField { field, reason } = e.kind() { warn!(field, reason, "Received invalid Fast-Path update"); processor_updates.push(UpdateKind::None); } else { diff --git a/crates/ironrdp-session/src/lib.rs b/crates/ironrdp-session/src/lib.rs index 54a3721c..8b3d62a4 100644 --- a/crates/ironrdp-session/src/lib.rs +++ b/crates/ironrdp-session/src/lib.rs @@ -110,7 +110,7 @@ pub trait SessionResultExt { impl SessionResultExt for SessionResult { fn with_context(self, context: &'static str) -> Self { self.map_err(|mut e| { - e.context = context; + e.set_context(context); e }) } diff --git a/crates/ironrdp-testsuite-core/tests/clipboard/mod.rs b/crates/ironrdp-testsuite-core/tests/clipboard/mod.rs index bbcee7af..dc0641f4 100644 --- a/crates/ironrdp-testsuite-core/tests/clipboard/mod.rs +++ b/crates/ironrdp-testsuite-core/tests/clipboard/mod.rs @@ -356,7 +356,7 @@ fn metafile_pdu_ms() { assert_eq!(metafile.y_ext, 423); // Just check some known arbitrary byte in raw metafile data - assert_eq!(metafile.data()[metafile.data().len() - 6], 0x03); + assert_eq!(metafile.data[metafile.data.len() - 6], 0x03); } else { panic!("Expected FormatDataResponse"); }; diff --git a/crates/ironrdp-testsuite-extra/tests/mod.rs b/crates/ironrdp-testsuite-extra/tests/mod.rs index 7127fa5f..8f5db6d6 100644 --- a/crates/ironrdp-testsuite-extra/tests/mod.rs +++ b/crates/ironrdp-testsuite-extra/tests/mod.rs @@ -87,7 +87,7 @@ async fn test_deactivation_reactivation() { desktop_size, enable_server_pointer, pointer_software_rendering, - } = connection_activation.state + } = connection_activation.connection_activation_state() { debug!(?desktop_size, "Deactivation-Reactivation Sequence completed"); // Update image size with the new desktop size. diff --git a/crates/ironrdp-web/src/error.rs b/crates/ironrdp-web/src/error.rs index 3264ae48..93772856 100644 --- a/crates/ironrdp-web/src/error.rs +++ b/crates/ironrdp-web/src/error.rs @@ -27,7 +27,7 @@ impl From for IronError { fn from(e: connector::ConnectorError) -> Self { use sspi::credssp::NStatusCode; - let kind = match e.kind { + let kind = match e.kind() { ConnectorErrorKind::Credssp(sspi::Error { nstatus: Some(NStatusCode::WRONG_PASSWORD), .. diff --git a/crates/ironrdp-web/src/session.rs b/crates/ironrdp-web/src/session.rs index a3e189a7..afcb387b 100644 --- a/crates/ironrdp-web/src/session.rs +++ b/crates/ironrdp-web/src/session.rs @@ -722,7 +722,7 @@ impl iron_remote_desktop::Session for Session { desktop_size, enable_server_pointer, pointer_software_rendering, - } = box_connection_activation.state + } = box_connection_activation.connection_activation_state() { debug!("Deactivation-Reactivation Sequence completed"); image = DecodedImage::new(PixelFormat::RgbA32, desktop_size.width, desktop_size.height); diff --git a/ffi/src/connector/activation.rs b/ffi/src/connector/activation.rs index 076315c6..d48bede6 100644 --- a/ffi/src/connector/activation.rs +++ b/ffi/src/connector/activation.rs @@ -16,7 +16,7 @@ pub mod ffi { impl ConnectionActivationSequence { pub fn get_state(&self) -> Box { - Box::new(ConnectionActivationState(self.0.state.clone())) + Box::new(ConnectionActivationState(self.0.connection_activation_state())) } pub fn next_pdu_hint<'a>(&'a self) -> Result>>, Box> { @@ -83,17 +83,17 @@ pub mod ffi { pub fn get_connection_finalization( &self, ) -> Result, Box> { - match &self.0 { + match self.0 { ironrdp::connector::connection_activation::ConnectionActivationState::ConnectionFinalization { io_channel_id, user_channel_id, desktop_size, connection_finalization, } => Ok(Box::new(ConnectionActivationStateConnectionFinalization { - io_channel_id: *io_channel_id, - user_channel_id: *user_channel_id, - desktop_size: *desktop_size, - connection_finalization: connection_finalization.clone(), + io_channel_id, + user_channel_id, + desktop_size, + connection_finalization, })), _ => Err(IncorrectEnumTypeError::on_variant("ConnectionFinalization") .of_enum("ConnectionActivationState") diff --git a/ffi/src/error.rs b/ffi/src/error.rs index 9312ba45..cf32007d 100644 --- a/ffi/src/error.rs +++ b/ffi/src/error.rs @@ -11,7 +11,7 @@ use self::ffi::IronRdpErrorKind; impl From for IronRdpErrorKind { fn from(val: ConnectorError) -> Self { - match val.kind { + match val.kind() { ironrdp::connector::ConnectorErrorKind::Encode(_) => IronRdpErrorKind::EncodeError, ironrdp::connector::ConnectorErrorKind::Decode(_) => IronRdpErrorKind::DecodeError, ironrdp::connector::ConnectorErrorKind::Credssp(_) => IronRdpErrorKind::CredsspError, From 2cedc05722b9321062d47ed9cc4e5d475bf272f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Larivi=C3=A8re?= Date: Thu, 30 Oct 2025 08:40:10 -0400 Subject: [PATCH 24/51] ci(npm): migrate publishing to OIDC authentication (#1026) Issue: DEVOPS-3952 --- .github/workflows/ci.yml | 30 +++++------- .github/workflows/coverage.yml | 12 ++--- .github/workflows/fuzz.yml | 18 +++---- .github/workflows/npm-publish.yml | 52 +++++++++----------- .github/workflows/nuget-publish.yml | 73 +++++++++++++--------------- .github/workflows/release-crates.yml | 7 ++- 6 files changed, 87 insertions(+), 105 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c21857bb..6b85c19d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ on: branches: - master pull_request: - types: [ opened, synchronize, reopened ] + types: [opened, synchronize, reopened] workflow_dispatch: env: @@ -56,12 +56,12 @@ jobs: checks: name: Checks [${{ matrix.os }}] + needs: [formatting] runs-on: ${{ matrix.runner }} - needs: formatting strategy: fail-fast: false matrix: - os: [ windows, linux, macos ] + os: [windows, linux, macos] include: - os: windows runner: windows-latest @@ -75,17 +75,17 @@ jobs: uses: actions/checkout@v4 - name: Install devel packages - if: runner.os == 'Linux' + if: ${{ runner.os == 'Linux' }} run: | sudo apt-get -y install libasound2-dev - name: Install NASM - if: runner.os == 'Windows' - shell: pwsh + if: ${{ runner.os == 'Windows' }} run: | choco install nasm $Env:PATH += ";$Env:ProgramFiles\NASM" echo "PATH=$Env:PATH" >> $Env:GITHUB_ENV + shell: pwsh - name: Rust cache uses: Swatinem/rust-cache@v2.7.3 @@ -118,8 +118,8 @@ jobs: fuzz: name: Fuzzing + needs: [formatting] runs-on: ubuntu-latest - needs: formatting steps: - uses: actions/checkout@v4 @@ -147,8 +147,8 @@ jobs: web: name: Web Client + needs: [formatting] runs-on: ubuntu-latest - needs: formatting steps: - uses: actions/checkout@v4 @@ -173,8 +173,8 @@ jobs: ffi: name: FFI + needs: [formatting] runs-on: ubuntu-latest - needs: formatting steps: - uses: actions/checkout@v4 @@ -202,20 +202,14 @@ jobs: success: name: Success - runs-on: ubuntu-latest if: ${{ always() }} - needs: - - formatting - - typos - - checks - - fuzz - - web - - ffi + needs: [formatting, typos, checks, fuzz, web, ffi] + runs-on: ubuntu-latest steps: - name: Check success - shell: pwsh run: | $results = '${{ toJSON(needs.*.result) }}' | ConvertFrom-Json $succeeded = $($results | Where { $_ -Ne "success" }).Count -Eq 0 exit $(if ($succeeded) { 0 } else { 1 }) + shell: pwsh diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 4e830be0..25aefe75 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -5,7 +5,7 @@ on: branches: - master pull_request: - types: [ opened, synchronize, reopened ] + types: [opened, synchronize, reopened] workflow_dispatch: env: @@ -32,19 +32,19 @@ jobs: run: cargo xtask cov install -v - name: Generate PR report - if: github.event.number != '' + if: ${{ github.event.number != '' }} + run: cargo xtask cov report-gh --repo "${{ github.repository }}" --pr "${{ github.event.number }}" -v env: GH_TOKEN: ${{ github.token }} - run: cargo xtask cov report-gh --repo "${{ github.repository }}" --pr "${{ github.event.number }}" -v - name: Configure Git Identity - if: github.ref == 'refs/heads/master' + if: ${{ github.ref == 'refs/heads/master' }} run: | git config --local user.name "github-actions[bot]" git config --local user.email "github-actions[bot]@users.noreply.github.com" - name: Update coverage data - if: github.ref == 'refs/heads/master' + if: ${{ github.ref == 'refs/heads/master' }} + run: cargo xtask cov update -v env: GH_TOKEN: ${{ secrets.DEVOLUTIONSBOT_TOKEN }} - run: cargo xtask cov update -v diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 71d6762c..26a9d855 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -36,12 +36,12 @@ jobs: fuzz: name: Fuzzing ${{ matrix.target }} + needs: [corpus-download] runs-on: ubuntu-latest - needs: corpus-download strategy: fail-fast: false matrix: - target: [ pdu_decoding, rle_decompression, bitmap_stream, cliprdr_format, channel_processing ] + target: [pdu_decoding, rle_decompression, bitmap_stream, cliprdr_format, channel_processing] steps: - uses: actions/checkout@v4 @@ -108,9 +108,9 @@ jobs: corpus-merge: name: Corpus merge artifacts - runs-on: ubuntu-latest - needs: fuzz if: ${{ always() && !cancelled() }} + needs: [fuzz] + runs-on: ubuntu-latest steps: - name: Merge Artifacts @@ -122,9 +122,9 @@ jobs: corpus-upload: name: Upload corpus - runs-on: ubuntu-latest - needs: corpus-merge if: ${{ always() && !cancelled() }} + needs: [corpus-merge] + runs-on: ubuntu-latest env: AZURE_STORAGE_KEY: ${{ secrets.CORPUS_AZURE_STORAGE_KEY }} @@ -156,13 +156,13 @@ jobs: notify: name: Notify failure - runs-on: ubuntu-latest if: ${{ always() && contains(needs.*.result, 'failure') && github.event_name == 'schedule' }} - needs: - - fuzz + needs: [fuzz] + runs-on: ubuntu-latest env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_ARCHITECTURE }} SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK + steps: - name: Send slack notification id: slack diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 2a559f72..dddf8fa6 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -21,7 +21,6 @@ jobs: steps: - name: Get dry run id: get-dry-run - shell: pwsh run: | $IsDryRun = '${{ github.event.inputs.dry-run }}' -Eq 'true' -Or '${{ github.event_name }}' -Eq 'schedule' @@ -30,13 +29,12 @@ jobs: } else { echo "dry-run=false" >> $Env:GITHUB_OUTPUT } + shell: pwsh build: name: Build package [${{matrix.library}}] + needs: [preflight] runs-on: ubuntu-latest - needs: - - preflight - strategy: fail-fast: false matrix: @@ -49,18 +47,17 @@ jobs: uses: actions/checkout@v4 - name: Setup wasm-pack - shell: bash run: | curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + shell: bash - name: Install dependencies - shell: pwsh run: | Set-Location -Path "./web-client/${{matrix.library}}/" npm install + shell: pwsh - name: Build package - shell: pwsh run: | Set-PSDebug -Trace 1 @@ -68,14 +65,15 @@ jobs: npm run build Set-Location -Path ./dist npm pack + shell: pwsh - name: Harvest package - shell: pwsh run: | Set-PSDebug -Trace 1 New-Item -ItemType "directory" -Path . -Name "npm-packages" Get-ChildItem -Path ./web-client/ -Recurse *.tgz | ForEach { Copy-Item $_ "./npm-packages" } + shell: pwsh - name: Upload package artifact uses: actions/upload-artifact@v4 @@ -85,8 +83,8 @@ jobs: npm-merge: name: Merge artifacts + needs: [build] runs-on: ubuntu-latest - needs: build steps: - name: Merge Artifacts @@ -98,12 +96,13 @@ jobs: publish: name: Publish package - runs-on: ubuntu-latest - if: github.event_name == 'workflow_dispatch' environment: publish - needs: - - preflight - - npm-merge + if: ${{ github.event_name == 'workflow_dispatch' }} + needs: [preflight, npm-merge] + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write steps: - name: Checkout repository @@ -117,12 +116,7 @@ jobs: name: npm path: npm-packages - - name: Prepare npm - shell: pwsh - run: npm config set "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" - - name: Publish - shell: pwsh run: | Set-PSDebug -Trace 1 @@ -168,15 +162,10 @@ jobs: $publishCmd = $publishCmd -Join ' ' Invoke-Expression $publishCmd } + shell: pwsh - name: Create version tags if: ${{ needs.preflight.outputs.dry-run == 'false' }} - shell: bash - env: - GIT_AUTHOR_NAME: github-actions - GIT_AUTHOR_EMAIL: github-actions@github.com - GIT_COMMITTER_NAME: github-actions - GIT_COMMITTER_EMAIL: github-actions@github.com run: | set -e @@ -202,6 +191,12 @@ jobs: git tag "$tag" "$GITHUB_SHA" git push origin "$tag" done + shell: bash + env: + GIT_AUTHOR_NAME: github-actions + GIT_AUTHOR_EMAIL: github-actions@github.com + GIT_COMMITTER_NAME: github-actions + GIT_COMMITTER_EMAIL: github-actions@github.com - name: Update Artifactory Cache if: ${{ needs.preflight.outputs.dry-run == 'false' }} @@ -213,14 +208,13 @@ jobs: notify: name: Notify failure - runs-on: ubuntu-latest if: ${{ always() && contains(needs.*.result, 'failure') && github.event_name == 'schedule' }} - needs: - - preflight - - build + needs: [preflight, build] + runs-on: ubuntu-latest env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_ARCHITECTURE }} SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK + steps: - name: Send slack notification id: slack diff --git a/.github/workflows/nuget-publish.yml b/.github/workflows/nuget-publish.yml index 267a0fe3..d14cd9f3 100644 --- a/.github/workflows/nuget-publish.yml +++ b/.github/workflows/nuget-publish.yml @@ -26,7 +26,6 @@ jobs: - name: Get dry run id: get-dry-run - shell: pwsh run: | $IsDryRun = '${{ github.event.inputs.dry-run }}' -Eq 'true' -Or '${{ github.event_name }}' -Eq 'schedule' @@ -35,26 +34,27 @@ jobs: } else { echo "dry-run=false" >> $Env:GITHUB_OUTPUT } + shell: pwsh - name: Get version id: get-version - shell: pwsh run: | $CsprojXml = [Xml] (Get-Content .\ffi\dotnet\Devolutions.IronRdp\Devolutions.IronRdp.csproj) $ProjectVersion = $CsprojXml.Project.PropertyGroup.Version | Select-Object -First 1 $PackageVersion = $ProjectVersion -Replace "^(\d+)\.(\d+)\.(\d+).(\d+)$", "`$1.`$2.`$3" echo "project-version=$ProjectVersion" >> $Env:GITHUB_OUTPUT echo "package-version=$PackageVersion" >> $Env:GITHUB_OUTPUT + shell: pwsh build-native: name: Native build + needs: [preflight] runs-on: ${{matrix.runner}} - needs: preflight strategy: fail-fast: false matrix: - os: [ win, osx, linux, ios, android ] - arch: [ x86, x64, arm, arm64 ] + os: [win, osx, linux, ios, android] + arch: [x86, x64, arm, arm64] include: - os: win runner: windows-2022 @@ -89,20 +89,20 @@ jobs: uses: actions/checkout@v4 - name: Configure Android NDK + if: ${{ matrix.os == 'android' }} uses: Devolutions/actions-public/cargo-android-ndk@v1 - if: matrix.os == 'android' with: android_api_level: "21" - name: Configure macOS deployement target if: ${{ matrix.os == 'osx' }} - shell: pwsh run: Write-Output "MACOSX_DEPLOYMENT_TARGET=10.10" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + shell: pwsh - name: Configure iOS deployement target if: ${{ matrix.os == 'ios' }} - shell: pwsh run: Write-Output "IPHONEOS_DEPLOYMENT_TARGET=12.1" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append + shell: pwsh - name: Update runner if: ${{ matrix.os == 'linux' }} @@ -110,12 +110,11 @@ jobs: - name: Install dependencies for rustls if: ${{ runner.os == 'Windows' }} - shell: pwsh run: | choco install ninja nasm # We need to add the NASM binary folder to the PATH manually. - # We don’t need to do that for ninja. + # We don't need to do that for ninja. Write-Output "PATH=$Env:PATH;$Env:ProgramFiles\NASM" >> $Env:GITHUB_ENV # libclang / LLVM is a requirement for AWS LC. @@ -125,6 +124,7 @@ jobs: # Install Visual Studio Developer PowerShell Module for cmdlets such as Enter-VsDevShell Install-Module VsDevShell -Force + shell: pwsh # No pre-generated bindings for Android and iOS. # https://aws.github.io/aws-lc-rs/platform_support.html#pre-generated-bindings @@ -141,19 +141,18 @@ jobs: sudo apt-get install gcc-multilib - name: Setup LLVM + if: ${{ matrix.os == 'linux' }} uses: Devolutions/actions-public/setup-llvm@v1 - if: matrix.os == 'linux' with: version: "18.1.8" - name: Setup CBake + if: ${{ matrix.os == 'linux' }} uses: Devolutions/actions-public/setup-cbake@v1 - if: matrix.os == 'linux' with: cargo_env_scripts: true - name: Build native lib (${{matrix.os}}-${{matrix.arch}}) - shell: pwsh run: | $DotNetOs = '${{matrix.os}}' $DotNetArch = '${{matrix.arch}}' @@ -210,6 +209,7 @@ jobs: $OutputPath = Join-Path "dependencies" "runtimes" $DotNetRid "native" New-Item -ItemType Directory -Path $OutputPath | Out-Null Copy-Item $OutputLibrary $(Join-Path $OutputPath $RenamedLibraryName) + shell: pwsh - name: Upload native components uses: actions/upload-artifact@v4 @@ -219,8 +219,8 @@ jobs: build-universal: name: Universal build + needs: [preflight, build-native] runs-on: ubuntu-22.04 - needs: [ preflight, build-native ] strategy: fail-fast: false matrix: @@ -239,7 +239,6 @@ jobs: path: dependencies/runtimes - name: Lipo native components - shell: pwsh run: | Set-Location "dependencies/runtimes" # No RID for universal binaries, see: https://github.com/dotnet/runtime/issues/53156 @@ -249,9 +248,9 @@ jobs: $LipoCmd = $(@('lipo', '-create', '-output', (Join-Path -Path $OutputPath -ChildPath "libDevolutionsIronRdp.dylib")) + $Libraries) -Join ' ' Write-Host $LipoCmd Invoke-Expression $LipoCmd + shell: pwsh - name: Framework - shell: pwsh if: ${{ matrix.os == 'ios' }} run: | $Version = '${{ needs.preflight.outputs.project-version }}' @@ -269,19 +268,19 @@ jobs: [xml] $InfoPlistXml = Get-Content (Join-Path "ffi" "dotnet" "Devolutions.IronRdp" "Info.plist") Select-Xml -xml $InfoPlistXml -XPath "/plist/dict/key[. = 'CFBundleIdentifier']/following-sibling::string[1]" | - %{ + %{ $_.Node.InnerXml = "com.devolutions.ironrdp" } Select-Xml -xml $InfoPlistXml -XPath "/plist/dict/key[. = 'CFBundleExecutable']/following-sibling::string[1]" | - %{ + %{ $_.Node.InnerXml = $BundleName } Select-Xml -xml $InfoPlistXml -XPath "/plist/dict/key[. = 'CFBundleVersion']/following-sibling::string[1]" | - %{ + %{ $_.Node.InnerXml = $Version } Select-Xml -xml $InfoPlistXml -XPath "/plist/dict/key[. = 'CFBundleShortVersionString']/following-sibling::string[1]" | - %{ + %{ $_.Node.InnerXml = $ShortVersion } @@ -294,6 +293,7 @@ jobs: # .NET XML document inserts two square brackets at the end of the DOCTYPE tag # It's perfectly valid XML, but we're dealing with plists here and dyld will not be able to read the file ((Get-Content -Path (Join-Path $FrameworkDir "Info.plist") -Raw) -Replace 'PropertyList-1.0.dtd"\[\]', 'PropertyList-1.0.dtd"') | Set-Content -Path (Join-Path $FrameworkDir "Info.plist") + shell: pwsh - name: Upload native components uses: actions/upload-artifact@v4 @@ -303,8 +303,8 @@ jobs: build-managed: name: Managed build + needs: [build-universal] runs-on: windows-2022 - needs: build-universal steps: - name: Check out ${{ github.repository }} @@ -317,9 +317,9 @@ jobs: run: dotnet workload install ios - name: Prepare dependencies - shell: pwsh run: | New-Item -ItemType Directory -Path "dependencies/runtimes" | Out-Null + shell: pwsh - name: Download native components uses: actions/download-artifact@v4 @@ -327,19 +327,19 @@ jobs: path: dependencies/runtimes - name: Rename dependencies - shell: pwsh run: | Set-Location "dependencies/runtimes" $(Get-Item ".\ironrdp-*") | ForEach-Object { Rename-Item $_ $_.Name.Replace("ironrdp-", "") } Get-ChildItem * -Recurse + shell: pwsh - name: Build Devolutions.IronRdp (managed) - shell: pwsh run: | # net8.0 target packaged as Devolutions.IronRdp dotnet build .\ffi\dotnet\Devolutions.IronRdp\Devolutions.IronRdp.csproj -c Release # net8.0-ios target packaged as Devolutions.IronRdp.iOS dotnet build .\ffi\dotnet\Devolutions.IronRdp\Devolutions.IronRdp.csproj -c Release /p:PackageId=Devolutions.IronRdp.iOS + shell: pwsh - name: Upload managed components uses: actions/upload-artifact@v4 @@ -349,12 +349,10 @@ jobs: publish: name: Publish NuGet package - runs-on: ubuntu-latest environment: nuget-publish - if: needs.preflight.outputs.dry-run == 'false' - needs: - - preflight - - build-managed + if: ${{ needs.preflight.outputs.dry-run == 'false' }} + needs: [preflight, build-managed] + runs-on: ubuntu-latest steps: - name: Download NuGet package artifact @@ -364,15 +362,14 @@ jobs: path: package - name: Publish to nuget.org - shell: pwsh run: | $Files = Get-ChildItem -Recurse package/*.nupkg foreach ($File in $Files) { $PushCmd = @( - 'dotnet', - 'nuget', - 'push', + 'dotnet', + 'nuget', + 'push', "$File", '--api-key', '${{ secrets.NUGET_API_KEY }}', @@ -385,19 +382,17 @@ jobs: $PushCmd = $PushCmd -Join ' ' Invoke-Expression $PushCmd } + shell: pwsh notify: name: Notify failure - runs-on: ubuntu-latest if: ${{ always() && contains(needs.*.result, 'failure') && github.event_name == 'schedule' }} - needs: - - preflight - - build-native - - build-universal - - build-managed + needs: [preflight, build-native, build-universal, build-managed] + runs-on: ubuntu-latest env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_ARCHITECTURE }} SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK + steps: - name: Send slack notification id: slack diff --git a/.github/workflows/release-crates.yml b/.github/workflows/release-crates.yml index 7465f76f..c03b09bd 100644 --- a/.github/workflows/release-crates.yml +++ b/.github/workflows/release-crates.yml @@ -14,9 +14,8 @@ jobs: # Create a PR with the new versions and changelog, preparing the next release. open-pr: name: Open release PR - runs-on: ubuntu-latest environment: cratesio-publish - + runs-on: ubuntu-latest concurrency: group: release-plz-${{ github.ref }} cancel-in-progress: false @@ -37,7 +36,6 @@ jobs: github-token: ${{ secrets.DEVOLUTIONSBOT_WRITE_TOKEN }} - name: Update fuzz/Cargo.lock - shell: pwsh if: ${{ steps.release-plz.outputs.did-open-pr == 'true' }} run: | $prRaw = '${{ steps.release-plz.outputs.pr }}' @@ -61,12 +59,13 @@ jobs: Write-Host "Update the release pull request" git push --force + shell: pwsh # Release unpublished packages. release: name: Release crates - runs-on: ubuntu-latest environment: cratesio-publish + runs-on: ubuntu-latest steps: - name: Checkout repository From d3e0cb17e1d1a638110cd8d31eba6ab1f3520dd2 Mon Sep 17 00:00:00 2001 From: "irvingouj@Devolutions" Date: Thu, 30 Oct 2025 12:38:41 -0400 Subject: [PATCH 25/51] feat(ffi): expose RDCleanPath (#1014) Add RDCleanPath support for Devolutions.IronRDP .NET package --- Cargo.lock | 2 + ffi/Cargo.toml | 2 + ...Devolutions.IronRdp.AvaloniaExample.csproj | 12 +- .../MainWindow.axaml.cs | 132 +++++- .../TokenGenerator.cs | 192 ++++++++ .../Devolutions.IronRdp.ConnectExample.csproj | 2 +- .../Program.cs | 4 +- .../Generated/CertificateChainIterator.cs | 109 +++++ .../Devolutions.IronRdp/Generated/Log.cs | 7 + .../Generated/RDCleanPathDetectionResult.cs | 129 ++++++ .../Generated/RDCleanPathPdu.cs | 424 ++++++++++++++++++ .../Generated/RDCleanPathResultType.cs | 20 + .../Generated/RawCertificateChainIterator.cs | 31 ++ .../Devolutions.IronRdp/Generated/RawLog.cs | 7 + .../RawRDCleanPathDetectionResult.cs | 36 ++ .../Generated/RawRDCleanPathPdu.cs | 96 ++++ .../Generated/RawRDCleanPathResultType.cs | 20 + ...CertificateChainIteratorBoxIronRdpError.cs | 46 ++ ...iResultBoxRDCleanPathPduBoxIronRdpError.cs | 46 ++ ...eanpathFfiResultBoxVecU8BoxIronRdpError.cs | 46 ++ ...ultRDCleanPathResultTypeBoxIronRdpError.cs | 46 ++ ...wRdcleanpathFfiResultU16BoxIronRdpError.cs | 46 ++ ...dcleanpathFfiResultUsizeBoxIronRdpError.cs | 46 ++ .../Devolutions.IronRdp/src/Connection.cs | 125 ++---- .../src/ConnectionHelpers.cs | 121 +++++ ffi/dotnet/Devolutions.IronRdp/src/Framed.cs | 41 ++ .../src/RDCleanPathConnection.cs | 200 +++++++++ .../src/WebsocketStream.cs | 212 +++++++++ ffi/src/clipboard/mod.rs | 4 +- ffi/src/connector/mod.rs | 4 +- ffi/src/error.rs | 33 +- ffi/src/lib.rs | 1 + ffi/src/log.rs | 2 +- ffi/src/rdcleanpath.rs | 268 +++++++++++ 34 files changed, 2385 insertions(+), 127 deletions(-) create mode 100644 ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/TokenGenerator.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/CertificateChainIterator.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RDCleanPathDetectionResult.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RDCleanPathPdu.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RDCleanPathResultType.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawCertificateChainIterator.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRDCleanPathDetectionResult.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRDCleanPathPdu.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRDCleanPathResultType.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxCertificateChainIteratorBoxIronRdpError.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRDCleanPathPduBoxIronRdpError.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxVecU8BoxIronRdpError.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultRDCleanPathResultTypeBoxIronRdpError.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultU16BoxIronRdpError.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultUsizeBoxIronRdpError.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/src/ConnectionHelpers.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/src/RDCleanPathConnection.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/src/WebsocketStream.cs create mode 100644 ffi/src/rdcleanpath.rs diff --git a/Cargo.lock b/Cargo.lock index ea165ee6..980260bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1527,6 +1527,7 @@ dependencies = [ name = "ffi" version = "0.0.0" dependencies = [ + "anyhow", "diplomat", "diplomat-runtime", "embed-resource", @@ -1534,6 +1535,7 @@ dependencies = [ "ironrdp-cliprdr-native", "ironrdp-core", "ironrdp-dvc-pipe-proxy", + "ironrdp-rdcleanpath", "sspi", "thiserror 2.0.17", "tracing", diff --git a/ffi/Cargo.toml b/ffi/Cargo.toml index 30d98d3d..22d5871e 100644 --- a/ffi/Cargo.toml +++ b/ffi/Cargo.toml @@ -18,10 +18,12 @@ ironrdp = { path = "../crates/ironrdp", features = ["session", "connector", "dvc ironrdp-cliprdr-native.path = "../crates/ironrdp-cliprdr-native" ironrdp-dvc-pipe-proxy.path = "../crates/ironrdp-dvc-pipe-proxy" ironrdp-core = { path = "../crates/ironrdp-core", features = ["alloc"] } +ironrdp-rdcleanpath.path = "../crates/ironrdp-rdcleanpath" sspi = { version = "0.16", features = ["network_client"] } thiserror = "2" tracing = { version = "0.1", features = ["log"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] } +anyhow = "1.0" [target.'cfg(windows)'.build-dependencies] embed-resource = "3.0" diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Devolutions.IronRdp.AvaloniaExample.csproj b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Devolutions.IronRdp.AvaloniaExample.csproj index e29164c4..802b8ad7 100644 --- a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Devolutions.IronRdp.AvaloniaExample.csproj +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Devolutions.IronRdp.AvaloniaExample.csproj @@ -10,18 +10,18 @@ - - - - + + + + - + - + diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs index 34148527..4ab8ef3f 100644 --- a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs @@ -7,6 +7,7 @@ using Avalonia.Threading; using System; using System.ComponentModel; using System.Diagnostics; +using System.IO; using System.Net.Security; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -22,7 +23,7 @@ public partial class MainWindow : Window readonly InputDatabase? _inputDatabase = InputDatabase.New(); ActiveStage? _activeStage; DecodedImage? _decodedImage; - Framed? _framed; + Framed? _framed; WinCliprdr? _cliprdr; private readonly RendererModel _renderModel; private Image? _imageControl; @@ -77,18 +78,49 @@ public partial class MainWindow : Window var username = Environment.GetEnvironmentVariable("IRONRDP_USERNAME"); var password = Environment.GetEnvironmentVariable("IRONRDP_PASSWORD"); - var domain = Environment.GetEnvironmentVariable("IRONRDP_DOMAIN"); + var domain = Environment.GetEnvironmentVariable("IRONRDP_DOMAIN"); // Optional var server = Environment.GetEnvironmentVariable("IRONRDP_SERVER"); + var portEnv = Environment.GetEnvironmentVariable("IRONRDP_PORT"); // Optional - if (username == null || password == null || domain == null || server == null) + // Gateway configuration (optional) + var gatewayUrl = Environment.GetEnvironmentVariable("IRONRDP_GATEWAY_URL"); + var gatewayToken = Environment.GetEnvironmentVariable("IRONRDP_GATEWAY_TOKEN"); + var tokengenUrl = Environment.GetEnvironmentVariable("IRONRDP_TOKENGEN_URL"); + + if (username == null || password == null || server == null) { var errorMessage = - "Please set the IRONRDP_USERNAME, IRONRDP_PASSWORD, IRONRDP_DOMAIN, and RONRDP_SERVER environment variables"; + "Please set the IRONRDP_USERNAME, IRONRDP_PASSWORD, and IRONRDP_SERVER environment variables"; Trace.TraceError(errorMessage); Close(); throw new InvalidProgramException(errorMessage); } + // Validate server is only domain or IP (no port allowed) + // i.e. "example.com" or "10.10.0.3" the port should go to the dedicated env var IRONRDP_PORT + if (server.Contains(':')) + { + var errorMessage = $"IRONRDP_SERVER must be a domain or IP address only, not '{server}'. Use IRONRDP_PORT for the port."; + Trace.TraceError(errorMessage); + Close(); + throw new InvalidProgramException(errorMessage); + } + + // Parse port from environment variable or use default + int port = 3389; + if (!string.IsNullOrEmpty(portEnv)) + { + if (!int.TryParse(portEnv, out port) || port <= 0 || port > 65535) + { + var errorMessage = $"IRONRDP_PORT must be a valid port number (1-65535), got '{portEnv}'"; + Trace.TraceError(errorMessage); + Close(); + throw new InvalidProgramException(errorMessage); + } + } + + Trace.TraceInformation($"Target server: {server}:{port}"); + var config = BuildConfig(username, password, domain, _renderModel.Width, _renderModel.Height); CliprdrBackendFactory? factory = null; @@ -106,15 +138,81 @@ public partial class MainWindow : Window BeforeConnectSetup(); Task.Run(async () => { - var (res, framed) = await Connection.Connect(config, server, factory); - this._decodedImage = DecodedImage.New(PixelFormat.RgbA32, res.GetDesktopSize().GetWidth(), - res.GetDesktopSize().GetHeight()); - this._activeStage = ActiveStage.New(res); - this._framed = framed; - ReadPduAndProcessActiveStage(); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + try { - HandleClipboardEvents(); + ConnectionResult res; + + // Determine connection mode: Gateway or Direct + if (!string.IsNullOrEmpty(gatewayUrl)) + { + Trace.TraceInformation("=== GATEWAY MODE ==="); + Trace.TraceInformation($"Gateway URL: {gatewayUrl}"); + Trace.TraceInformation($"Destination: {server}:{port}"); + + var tokenGen = new TokenGenerator(tokengenUrl ?? "http://localhost:8080"); + + // Generate RDP token if not provided + if (string.IsNullOrEmpty(gatewayToken)) + { + Trace.TraceInformation("No RDP token provided, generating token..."); + + try + { + gatewayToken = await tokenGen.GenerateRdpTlsToken( + dstHost: server!, + proxyUser: string.IsNullOrEmpty(domain) ? username : $"{username}@{domain}", + proxyPassword: password!, + destUser: username!, + destPassword: password! + ); + Trace.TraceInformation($"RDP token generated successfully (length: {gatewayToken.Length})"); + } + catch (Exception ex) + { + Trace.TraceError($"Failed to generate RDP token: {ex.Message}"); + Trace.TraceInformation("Make sure tokengen server is running:"); + Trace.TraceInformation($" cargo run --manifest-path tools/tokengen/Cargo.toml -- server"); + throw; + } + } + + // Connect via gateway - destination needs "hostname:port" format for RDCleanPath + string destination = $"{server}:{port}"; + + var (gatewayRes, gatewayFramed) = await RDCleanPathConnection.ConnectRDCleanPath( + config, gatewayUrl, gatewayToken!, destination, null, factory); + res = gatewayRes; + this._framed = new Framed(gatewayFramed.GetInner().Item1); + + Trace.TraceInformation("=== GATEWAY CONNECTION SUCCESSFUL ==="); + } + else + { + Trace.TraceInformation("=== DIRECT MODE ==="); + + // Direct connection (original behavior) + var (directRes, directFramed) = await Connection.Connect(config, server, factory, port); + res = directRes; + this._framed = new Framed(directFramed.GetInner().Item1); + + Trace.TraceInformation("=== DIRECT CONNECTION SUCCESSFUL ==="); + } + + this._decodedImage = DecodedImage.New(PixelFormat.RgbA32, res.GetDesktopSize().GetWidth(), + res.GetDesktopSize().GetHeight()); + this._activeStage = ActiveStage.New(res); + ReadPduAndProcessActiveStage(); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + HandleClipboardEvents(); + } + } + catch (Exception ex) + { + Trace.TraceError($"Connection failed: {ex.Message}"); + Trace.TraceError($"Stack trace: {ex.StackTrace}"); + throw; } }); } @@ -260,12 +358,16 @@ public partial class MainWindow : Window }); } - private static Config BuildConfig(string username, string password, string domain, int width, int height) + private static Config BuildConfig(string username, string password, string? domain, int width, int height) { ConfigBuilder configBuilder = ConfigBuilder.New(); configBuilder.WithUsernameAndPassword(username, password); - configBuilder.SetDomain(domain); + if (domain != null) + { + configBuilder.SetDomain(domain); + } + configBuilder.SetDesktopSize((ushort)height, (ushort)width); configBuilder.SetClientName("IronRdp"); configBuilder.SetClientDir("C:\\"); @@ -418,7 +520,7 @@ public partial class MainWindow : Window var writeBuf = WriteBuf.New(); while (true) { - await Connection.SingleSequenceStep(activationSequence, writeBuf,_framed!); + await Connection.SingleSequenceStep(activationSequence, writeBuf, _framed!); if (activationSequence.GetState().GetType() != ConnectionActivationStateType.Finalized) continue; diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/TokenGenerator.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/TokenGenerator.cs new file mode 100644 index 00000000..00946c60 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/TokenGenerator.cs @@ -0,0 +1,192 @@ +using System; +using System.Net.Http; +using System.Net.Http.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace Devolutions.IronRdp.AvaloniaExample; + +/// +/// Client for requesting JWT tokens from a Devolutions Gateway tokengen server. +/// +public class TokenGenerator : IDisposable +{ + private readonly HttpClient _client; + private readonly string _tokengenUrl; + + /// + /// Creates a new TokenGenerator instance. + /// + /// The base URL of the tokengen server (e.g., "http://localhost:8080") + public TokenGenerator(string tokengenUrl = "http://localhost:8080") + { + _tokengenUrl = tokengenUrl; + _client = new HttpClient + { + Timeout = TimeSpan.FromSeconds(30) + }; + } + + /// + /// Generates an RDP token with credential injection for gateway-based connections. + /// + /// Destination RDP server (e.g., "10.10.0.3:3389") + /// Gateway proxy username + /// Gateway proxy password + /// Destination RDP server username + /// Destination RDP server password + /// Optional session UUID + /// Token validity in seconds (default: 3600) + /// A JWT token string + public async Task GenerateRdpTlsToken( + string dstHost, + string proxyUser, + string proxyPassword, + string destUser, + string destPassword, + string? jetAid = null, + int validityDuration = 3600) + { + var request = new RdpTlsTokenRequest + { + DstHst = dstHost, + PrxUsr = proxyUser, + PrxPwd = proxyPassword, + DstUsr = destUser, + DstPwd = destPassword, + JetAid = jetAid, + ValidityDuration = validityDuration + }; + + try + { + var response = await _client.PostAsJsonAsync($"{_tokengenUrl}/rdp_tls", request); + response.EnsureSuccessStatusCode(); + + var result = await response.Content.ReadFromJsonAsync(); + if (result?.Token == null) + { + throw new Exception("Token generation failed: Empty response"); + } + + return result.Token; + } + catch (HttpRequestException ex) + { + throw new Exception($"Failed to connect to tokengen server at {_tokengenUrl}: {ex.Message}", ex); + } + catch (TaskCanceledException ex) + { + throw new Exception($"Token generation request timed out: {ex.Message}", ex); + } + } + + /// + /// Generates a forward mode token for simple RDP forwarding without credential injection. + /// + /// Destination host + /// Application protocol (default: "rdp") + /// Enable recording + /// Token validity in seconds (default: 3600) + /// A JWT token string + public async Task GenerateForwardToken( + string dstHost, + string jetAp = "rdp", + bool jetRec = false, + int validityDuration = 3600) + { + var request = new ForwardTokenRequest + { + DstHst = dstHost, + JetAp = jetAp, + JetRec = jetRec, + ValidityDuration = validityDuration + }; + + try + { + var response = await _client.PostAsJsonAsync($"{_tokengenUrl}/forward", request); + response.EnsureSuccessStatusCode(); + + var result = await response.Content.ReadFromJsonAsync(); + if (result?.Token == null) + { + throw new Exception("Token generation failed: Empty response"); + } + + return result.Token; + } + catch (HttpRequestException ex) + { + throw new Exception($"Failed to connect to tokengen server at {_tokengenUrl}: {ex.Message}", ex); + } + } + + /// + /// Checks if the tokengen server is reachable. + /// + /// True if server is reachable, false otherwise + public async Task IsServerReachable() + { + try + { + var response = await _client.GetAsync(_tokengenUrl); + return response.IsSuccessStatusCode || response.StatusCode == System.Net.HttpStatusCode.NotFound; + } + catch + { + return false; + } + } + + public void Dispose() + { + _client?.Dispose(); + } + + // Request/Response DTOs + private class RdpTlsTokenRequest + { + [JsonPropertyName("dst_hst")] + public string DstHst { get; set; } = string.Empty; + + [JsonPropertyName("prx_usr")] + public string PrxUsr { get; set; } = string.Empty; + + [JsonPropertyName("prx_pwd")] + public string PrxPwd { get; set; } = string.Empty; + + [JsonPropertyName("dst_usr")] + public string DstUsr { get; set; } = string.Empty; + + [JsonPropertyName("dst_pwd")] + public string DstPwd { get; set; } = string.Empty; + + [JsonPropertyName("jet_aid")] + public string? JetAid { get; set; } + + [JsonPropertyName("validity_duration")] + public int ValidityDuration { get; set; } + } + + private class ForwardTokenRequest + { + [JsonPropertyName("dst_hst")] + public string DstHst { get; set; } = string.Empty; + + [JsonPropertyName("jet_ap")] + public string JetAp { get; set; } = "rdp"; + + [JsonPropertyName("jet_rec")] + public bool JetRec { get; set; } + + [JsonPropertyName("validity_duration")] + public int ValidityDuration { get; set; } + } + + private class TokenResponse + { + [JsonPropertyName("token")] + public string? Token { get; set; } + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Devolutions.IronRdp.ConnectExample.csproj b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Devolutions.IronRdp.ConnectExample.csproj index 1ef474af..61014ed1 100644 --- a/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Devolutions.IronRdp.ConnectExample.csproj +++ b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Devolutions.IronRdp.ConnectExample.csproj @@ -16,7 +16,7 @@ https://learn.microsoft.com/en-us/dotnet/api/system.drawing?view=net-8.0 --> - + diff --git a/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Program.cs b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Program.cs index b685455c..3c85f258 100644 --- a/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Program.cs +++ b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Program.cs @@ -23,7 +23,7 @@ namespace Devolutions.IronRdp.ConnectExample try { - var (res, framed) = await Connection.Connect(buildConfig(serverName, username, password, domain, 1980, 1080), serverName, null); + var (res, framed) = await Connection.Connect(buildConfig(username, password, domain, 1980, 1080), serverName, null); var decodedImage = DecodedImage.New(PixelFormat.RgbA32, res.GetDesktopSize().GetWidth(), res.GetDesktopSize().GetHeight()); var activeState = ActiveStage.New(res); var keepLooping = true; @@ -175,7 +175,7 @@ namespace Devolutions.IronRdp.ConnectExample Console.WriteLine(" --help Show this message and exit."); } - private static Config buildConfig(string servername, string username, string password, string domain, int width, int height) + private static Config buildConfig(string username, string password, string domain, int width, int height) { ConfigBuilder configBuilder = ConfigBuilder.New(); diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/CertificateChainIterator.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/CertificateChainIterator.cs new file mode 100644 index 00000000..9830d732 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/CertificateChainIterator.cs @@ -0,0 +1,109 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public partial class CertificateChainIterator: IDisposable +{ + private unsafe Raw.CertificateChainIterator* _inner; + + /// + /// Creates a managed CertificateChainIterator from a raw handle. + /// + /// + /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). + ///
+ /// This constructor assumes the raw struct is allocated on Rust side. + /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. + ///
+ public unsafe CertificateChainIterator(Raw.CertificateChainIterator* handle) + { + _inner = handle; + } + + /// + /// A VecU8 allocated on Rust side. + /// + public VecU8? Next() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("CertificateChainIterator"); + } + Raw.VecU8* retVal = Raw.CertificateChainIterator.Next(_inner); + if (retVal == null) + { + return null; + } + return new VecU8(retVal); + } + } + + public nuint Len() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("CertificateChainIterator"); + } + nuint retVal = Raw.CertificateChainIterator.Len(_inner); + return retVal; + } + } + + public bool IsEmpty() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("CertificateChainIterator"); + } + bool retVal = Raw.CertificateChainIterator.IsEmpty(_inner); + return retVal; + } + } + + /// + /// Returns the underlying raw handle. + /// + public unsafe Raw.CertificateChainIterator* AsFFI() + { + return _inner; + } + + /// + /// Destroys the underlying object immediately. + /// + public void Dispose() + { + unsafe + { + if (_inner == null) + { + return; + } + + Raw.CertificateChainIterator.Destroy(_inner); + _inner = null; + + GC.SuppressFinalize(this); + } + } + + ~CertificateChainIterator() + { + Dispose(); + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/Log.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/Log.cs index ec2769f3..c0e3af52 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/Log.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/Log.cs @@ -29,6 +29,13 @@ public partial class Log: IDisposable _inner = handle; } + /// + /// # Panics + /// + /// + /// - Panics if log directory creation fails. + /// - Panics if tracing initialization fails. + /// public static void InitWithEnv() { unsafe diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RDCleanPathDetectionResult.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RDCleanPathDetectionResult.cs new file mode 100644 index 00000000..644c997e --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RDCleanPathDetectionResult.cs @@ -0,0 +1,129 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public partial class RDCleanPathDetectionResult: IDisposable +{ + private unsafe Raw.RDCleanPathDetectionResult* _inner; + + public nuint TotalLength + { + get + { + return GetTotalLength(); + } + } + + /// + /// Creates a managed RDCleanPathDetectionResult from a raw handle. + /// + /// + /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). + ///
+ /// This constructor assumes the raw struct is allocated on Rust side. + /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. + ///
+ public unsafe RDCleanPathDetectionResult(Raw.RDCleanPathDetectionResult* handle) + { + _inner = handle; + } + + public bool IsDetected() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RDCleanPathDetectionResult"); + } + bool retVal = Raw.RDCleanPathDetectionResult.IsDetected(_inner); + return retVal; + } + } + + public bool IsNotEnoughBytes() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RDCleanPathDetectionResult"); + } + bool retVal = Raw.RDCleanPathDetectionResult.IsNotEnoughBytes(_inner); + return retVal; + } + } + + public bool IsFailed() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RDCleanPathDetectionResult"); + } + bool retVal = Raw.RDCleanPathDetectionResult.IsFailed(_inner); + return retVal; + } + } + + /// + public nuint GetTotalLength() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RDCleanPathDetectionResult"); + } + Raw.RdcleanpathFfiResultUsizeBoxIronRdpError result = Raw.RDCleanPathDetectionResult.GetTotalLength(_inner); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + nuint retVal = result.Ok; + return retVal; + } + } + + /// + /// Returns the underlying raw handle. + /// + public unsafe Raw.RDCleanPathDetectionResult* AsFFI() + { + return _inner; + } + + /// + /// Destroys the underlying object immediately. + /// + public void Dispose() + { + unsafe + { + if (_inner == null) + { + return; + } + + Raw.RDCleanPathDetectionResult.Destroy(_inner); + _inner = null; + + GC.SuppressFinalize(this); + } + } + + ~RDCleanPathDetectionResult() + { + Dispose(); + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RDCleanPathPdu.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RDCleanPathPdu.cs new file mode 100644 index 00000000..b4c5d627 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RDCleanPathPdu.cs @@ -0,0 +1,424 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public partial class RDCleanPathPdu: IDisposable +{ + private unsafe Raw.RDCleanPathPdu* _inner; + + public ushort ErrorCode + { + get + { + return GetErrorCode(); + } + } + + public string ErrorMessage + { + get + { + return GetErrorMessage(); + } + } + + public ushort HttpStatusCode + { + get + { + return GetHttpStatusCode(); + } + } + + public string ServerAddr + { + get + { + return GetServerAddr(); + } + } + + public CertificateChainIterator ServerCertChain + { + get + { + return GetServerCertChain(); + } + } + + public RDCleanPathResultType Type + { + get + { + return GetType(); + } + } + + public VecU8 X224Response + { + get + { + return GetX224Response(); + } + } + + /// + /// Creates a managed RDCleanPathPdu from a raw handle. + /// + /// + /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). + ///
+ /// This constructor assumes the raw struct is allocated on Rust side. + /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. + ///
+ public unsafe RDCleanPathPdu(Raw.RDCleanPathPdu* handle) + { + _inner = handle; + } + + /// + /// Creates a new RDCleanPath request PDU + /// + /// + /// # Arguments + /// * `x224_pdu` - The X.224 Connection Request PDU bytes + /// * `destination` - The destination RDP server address (e.g., "10.10.0.3:3389") + /// * `proxy_auth` - The JWT authentication token + /// * `pcb` - Optional preconnection blob (for Hyper-V VM connections, empty string if not needed) + /// + /// + /// + /// A RDCleanPathPdu allocated on Rust side. + /// + public static RDCleanPathPdu NewRequest(byte[] x224Pdu, string destination, string proxyAuth, string pcb) + { + unsafe + { + byte[] destinationBuf = DiplomatUtils.StringToUtf8(destination); + byte[] proxyAuthBuf = DiplomatUtils.StringToUtf8(proxyAuth); + byte[] pcbBuf = DiplomatUtils.StringToUtf8(pcb); + nuint x224PduLength = (nuint)x224Pdu.Length; + nuint destinationBufLength = (nuint)destinationBuf.Length; + nuint proxyAuthBufLength = (nuint)proxyAuthBuf.Length; + nuint pcbBufLength = (nuint)pcbBuf.Length; + fixed (byte* x224PduPtr = x224Pdu) + { + fixed (byte* destinationBufPtr = destinationBuf) + { + fixed (byte* proxyAuthBufPtr = proxyAuthBuf) + { + fixed (byte* pcbBufPtr = pcbBuf) + { + Raw.RdcleanpathFfiResultBoxRDCleanPathPduBoxIronRdpError result = Raw.RDCleanPathPdu.NewRequest(x224PduPtr, x224PduLength, destinationBufPtr, destinationBufLength, proxyAuthBufPtr, proxyAuthBufLength, pcbBufPtr, pcbBufLength); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.RDCleanPathPdu* retVal = result.Ok; + return new RDCleanPathPdu(retVal); + } + } + } + } + } + } + + /// + /// Decodes a RDCleanPath PDU from DER-encoded bytes + /// + /// + /// + /// A RDCleanPathPdu allocated on Rust side. + /// + public static RDCleanPathPdu FromDer(byte[] bytes) + { + unsafe + { + nuint bytesLength = (nuint)bytes.Length; + fixed (byte* bytesPtr = bytes) + { + Raw.RdcleanpathFfiResultBoxRDCleanPathPduBoxIronRdpError result = Raw.RDCleanPathPdu.FromDer(bytesPtr, bytesLength); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.RDCleanPathPdu* retVal = result.Ok; + return new RDCleanPathPdu(retVal); + } + } + } + + /// + /// Encodes the RDCleanPath PDU to DER-encoded bytes + /// + /// + /// + /// A VecU8 allocated on Rust side. + /// + public VecU8 ToDer() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RDCleanPathPdu"); + } + Raw.RdcleanpathFfiResultBoxVecU8BoxIronRdpError result = Raw.RDCleanPathPdu.ToDer(_inner); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.VecU8* retVal = result.Ok; + return new VecU8(retVal); + } + } + + /// + /// Detects if the bytes contain a valid RDCleanPath PDU and returns detection result + /// + /// + /// A RDCleanPathDetectionResult allocated on Rust side. + /// + public static RDCleanPathDetectionResult Detect(byte[] bytes) + { + unsafe + { + nuint bytesLength = (nuint)bytes.Length; + fixed (byte* bytesPtr = bytes) + { + Raw.RDCleanPathDetectionResult* retVal = Raw.RDCleanPathPdu.Detect(bytesPtr, bytesLength); + return new RDCleanPathDetectionResult(retVal); + } + } + } + + /// + /// Gets the type of this RDCleanPath PDU + /// + /// + /// + /// A RDCleanPathResultType allocated on C# side. + /// + public RDCleanPathResultType GetType() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RDCleanPathPdu"); + } + Raw.RdcleanpathFfiResultRDCleanPathResultTypeBoxIronRdpError result = Raw.RDCleanPathPdu.GetType(_inner); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.RDCleanPathResultType retVal = result.Ok; + return (RDCleanPathResultType)retVal; + } + } + + /// + /// Gets the X.224 connection response bytes (for Response or NegotiationError variants) + /// + /// + /// + /// A VecU8 allocated on Rust side. + /// + public VecU8 GetX224Response() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RDCleanPathPdu"); + } + Raw.RdcleanpathFfiResultBoxVecU8BoxIronRdpError result = Raw.RDCleanPathPdu.GetX224Response(_inner); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.VecU8* retVal = result.Ok; + return new VecU8(retVal); + } + } + + /// + /// Gets the server certificate chain (for Response variant) + /// Returns a vector iterator of certificate bytes + /// + /// + /// + /// A CertificateChainIterator allocated on Rust side. + /// + public CertificateChainIterator GetServerCertChain() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RDCleanPathPdu"); + } + Raw.RdcleanpathFfiResultBoxCertificateChainIteratorBoxIronRdpError result = Raw.RDCleanPathPdu.GetServerCertChain(_inner); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.CertificateChainIterator* retVal = result.Ok; + return new CertificateChainIterator(retVal); + } + } + + /// + /// Gets the server address string (for Response variant) + /// + public void GetServerAddr(DiplomatWriteable writeable) + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RDCleanPathPdu"); + } + Raw.RDCleanPathPdu.GetServerAddr(_inner, &writeable); + } + } + + /// + /// Gets the server address string (for Response variant) + /// + public string GetServerAddr() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RDCleanPathPdu"); + } + DiplomatWriteable writeable = new DiplomatWriteable(); + Raw.RDCleanPathPdu.GetServerAddr(_inner, &writeable); + string retVal = writeable.ToUnicode(); + writeable.Dispose(); + return retVal; + } + } + + /// + /// Gets error message (for GeneralError variant) + /// + public void GetErrorMessage(DiplomatWriteable writeable) + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RDCleanPathPdu"); + } + Raw.RDCleanPathPdu.GetErrorMessage(_inner, &writeable); + } + } + + /// + /// Gets error message (for GeneralError variant) + /// + public string GetErrorMessage() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RDCleanPathPdu"); + } + DiplomatWriteable writeable = new DiplomatWriteable(); + Raw.RDCleanPathPdu.GetErrorMessage(_inner, &writeable); + string retVal = writeable.ToUnicode(); + writeable.Dispose(); + return retVal; + } + } + + /// + /// Gets the error code (for GeneralError variant) + /// + /// + public ushort GetErrorCode() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RDCleanPathPdu"); + } + Raw.RdcleanpathFfiResultU16BoxIronRdpError result = Raw.RDCleanPathPdu.GetErrorCode(_inner); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + ushort retVal = result.Ok; + return retVal; + } + } + + /// + /// Gets the HTTP status code if present (for GeneralError variant) + /// Returns error if not present or not a GeneralError variant + /// + /// + public ushort GetHttpStatusCode() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("RDCleanPathPdu"); + } + Raw.RdcleanpathFfiResultU16BoxIronRdpError result = Raw.RDCleanPathPdu.GetHttpStatusCode(_inner); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + ushort retVal = result.Ok; + return retVal; + } + } + + /// + /// Returns the underlying raw handle. + /// + public unsafe Raw.RDCleanPathPdu* AsFFI() + { + return _inner; + } + + /// + /// Destroys the underlying object immediately. + /// + public void Dispose() + { + unsafe + { + if (_inner == null) + { + return; + } + + Raw.RDCleanPathPdu.Destroy(_inner); + _inner = null; + + GC.SuppressFinalize(this); + } + } + + ~RDCleanPathPdu() + { + Dispose(); + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RDCleanPathResultType.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RDCleanPathResultType.cs new file mode 100644 index 00000000..4145574e --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RDCleanPathResultType.cs @@ -0,0 +1,20 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public enum RDCleanPathResultType +{ + Request = 0, + Response = 1, + GeneralError = 2, + NegotiationError = 3, +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawCertificateChainIterator.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawCertificateChainIterator.cs new file mode 100644 index 00000000..c451aa99 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawCertificateChainIterator.cs @@ -0,0 +1,31 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct CertificateChainIterator +{ + private const string NativeLib = "DevolutionsIronRdp"; + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "CertificateChainIterator_next", ExactSpelling = true)] + public static unsafe extern VecU8* Next(CertificateChainIterator* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "CertificateChainIterator_len", ExactSpelling = true)] + public static unsafe extern nuint Len(CertificateChainIterator* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "CertificateChainIterator_is_empty", ExactSpelling = true)] + [return: MarshalAs(UnmanagedType.U1)] + public static unsafe extern bool IsEmpty(CertificateChainIterator* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "CertificateChainIterator_destroy", ExactSpelling = true)] + public static unsafe extern void Destroy(CertificateChainIterator* self); +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawLog.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawLog.cs index e1f649b9..40482702 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawLog.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawLog.cs @@ -16,6 +16,13 @@ public partial struct Log { private const string NativeLib = "DevolutionsIronRdp"; + /// + /// # Panics + /// + /// + /// - Panics if log directory creation fails. + /// - Panics if tracing initialization fails. + /// [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Log_init_with_env", ExactSpelling = true)] public static unsafe extern void InitWithEnv(); diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRDCleanPathDetectionResult.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRDCleanPathDetectionResult.cs new file mode 100644 index 00000000..a5f7d88d --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRDCleanPathDetectionResult.cs @@ -0,0 +1,36 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct RDCleanPathDetectionResult +{ + private const string NativeLib = "DevolutionsIronRdp"; + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RDCleanPathDetectionResult_is_detected", ExactSpelling = true)] + [return: MarshalAs(UnmanagedType.U1)] + public static unsafe extern bool IsDetected(RDCleanPathDetectionResult* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RDCleanPathDetectionResult_is_not_enough_bytes", ExactSpelling = true)] + [return: MarshalAs(UnmanagedType.U1)] + public static unsafe extern bool IsNotEnoughBytes(RDCleanPathDetectionResult* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RDCleanPathDetectionResult_is_failed", ExactSpelling = true)] + [return: MarshalAs(UnmanagedType.U1)] + public static unsafe extern bool IsFailed(RDCleanPathDetectionResult* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RDCleanPathDetectionResult_get_total_length", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultUsizeBoxIronRdpError GetTotalLength(RDCleanPathDetectionResult* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RDCleanPathDetectionResult_destroy", ExactSpelling = true)] + public static unsafe extern void Destroy(RDCleanPathDetectionResult* self); +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRDCleanPathPdu.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRDCleanPathPdu.cs new file mode 100644 index 00000000..5710356a --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRDCleanPathPdu.cs @@ -0,0 +1,96 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct RDCleanPathPdu +{ + private const string NativeLib = "DevolutionsIronRdp"; + + /// + /// Creates a new RDCleanPath request PDU + /// + /// + /// # Arguments + /// * `x224_pdu` - The X.224 Connection Request PDU bytes + /// * `destination` - The destination RDP server address (e.g., "10.10.0.3:3389") + /// * `proxy_auth` - The JWT authentication token + /// * `pcb` - Optional preconnection blob (for Hyper-V VM connections, empty string if not needed) + /// + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RDCleanPathPdu_new_request", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultBoxRDCleanPathPduBoxIronRdpError NewRequest(byte* x224Pdu, nuint x224PduSz, byte* destination, nuint destinationSz, byte* proxyAuth, nuint proxyAuthSz, byte* pcb, nuint pcbSz); + + /// + /// Decodes a RDCleanPath PDU from DER-encoded bytes + /// + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RDCleanPathPdu_from_der", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultBoxRDCleanPathPduBoxIronRdpError FromDer(byte* bytes, nuint bytesSz); + + /// + /// Encodes the RDCleanPath PDU to DER-encoded bytes + /// + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RDCleanPathPdu_to_der", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultBoxVecU8BoxIronRdpError ToDer(RDCleanPathPdu* self); + + /// + /// Detects if the bytes contain a valid RDCleanPath PDU and returns detection result + /// + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RDCleanPathPdu_detect", ExactSpelling = true)] + public static unsafe extern RDCleanPathDetectionResult* Detect(byte* bytes, nuint bytesSz); + + /// + /// Gets the type of this RDCleanPath PDU + /// + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RDCleanPathPdu_get_type", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultRDCleanPathResultTypeBoxIronRdpError GetType(RDCleanPathPdu* self); + + /// + /// Gets the X.224 connection response bytes (for Response or NegotiationError variants) + /// + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RDCleanPathPdu_get_x224_response", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultBoxVecU8BoxIronRdpError GetX224Response(RDCleanPathPdu* self); + + /// + /// Gets the server certificate chain (for Response variant) + /// Returns a vector iterator of certificate bytes + /// + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RDCleanPathPdu_get_server_cert_chain", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultBoxCertificateChainIteratorBoxIronRdpError GetServerCertChain(RDCleanPathPdu* self); + + /// + /// Gets the server address string (for Response variant) + /// + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RDCleanPathPdu_get_server_addr", ExactSpelling = true)] + public static unsafe extern void GetServerAddr(RDCleanPathPdu* self, DiplomatWriteable* writeable); + + /// + /// Gets error message (for GeneralError variant) + /// + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RDCleanPathPdu_get_error_message", ExactSpelling = true)] + public static unsafe extern void GetErrorMessage(RDCleanPathPdu* self, DiplomatWriteable* writeable); + + /// + /// Gets the error code (for GeneralError variant) + /// + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RDCleanPathPdu_get_error_code", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultU16BoxIronRdpError GetErrorCode(RDCleanPathPdu* self); + + /// + /// Gets the HTTP status code if present (for GeneralError variant) + /// Returns error if not present or not a GeneralError variant + /// + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RDCleanPathPdu_get_http_status_code", ExactSpelling = true)] + public static unsafe extern RdcleanpathFfiResultU16BoxIronRdpError GetHttpStatusCode(RDCleanPathPdu* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "RDCleanPathPdu_destroy", ExactSpelling = true)] + public static unsafe extern void Destroy(RDCleanPathPdu* self); +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRDCleanPathResultType.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRDCleanPathResultType.cs new file mode 100644 index 00000000..393d6509 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRDCleanPathResultType.cs @@ -0,0 +1,20 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +public enum RDCleanPathResultType +{ + Request = 0, + Response = 1, + GeneralError = 2, + NegotiationError = 3, +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxCertificateChainIteratorBoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxCertificateChainIteratorBoxIronRdpError.cs new file mode 100644 index 00000000..01cb3590 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxCertificateChainIteratorBoxIronRdpError.cs @@ -0,0 +1,46 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct RdcleanpathFfiResultBoxCertificateChainIteratorBoxIronRdpError +{ + [StructLayout(LayoutKind.Explicit)] + private unsafe struct InnerUnion + { + [FieldOffset(0)] + internal CertificateChainIterator* ok; + [FieldOffset(0)] + internal IronRdpError* err; + } + + private InnerUnion _inner; + + [MarshalAs(UnmanagedType.U1)] + public bool isOk; + + public unsafe CertificateChainIterator* Ok + { + get + { + return _inner.ok; + } + } + + public unsafe IronRdpError* Err + { + get + { + return _inner.err; + } + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRDCleanPathPduBoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRDCleanPathPduBoxIronRdpError.cs new file mode 100644 index 00000000..c9f33a5d --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxRDCleanPathPduBoxIronRdpError.cs @@ -0,0 +1,46 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct RdcleanpathFfiResultBoxRDCleanPathPduBoxIronRdpError +{ + [StructLayout(LayoutKind.Explicit)] + private unsafe struct InnerUnion + { + [FieldOffset(0)] + internal RDCleanPathPdu* ok; + [FieldOffset(0)] + internal IronRdpError* err; + } + + private InnerUnion _inner; + + [MarshalAs(UnmanagedType.U1)] + public bool isOk; + + public unsafe RDCleanPathPdu* Ok + { + get + { + return _inner.ok; + } + } + + public unsafe IronRdpError* Err + { + get + { + return _inner.err; + } + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxVecU8BoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxVecU8BoxIronRdpError.cs new file mode 100644 index 00000000..7cce2e6a --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultBoxVecU8BoxIronRdpError.cs @@ -0,0 +1,46 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct RdcleanpathFfiResultBoxVecU8BoxIronRdpError +{ + [StructLayout(LayoutKind.Explicit)] + private unsafe struct InnerUnion + { + [FieldOffset(0)] + internal VecU8* ok; + [FieldOffset(0)] + internal IronRdpError* err; + } + + private InnerUnion _inner; + + [MarshalAs(UnmanagedType.U1)] + public bool isOk; + + public unsafe VecU8* Ok + { + get + { + return _inner.ok; + } + } + + public unsafe IronRdpError* Err + { + get + { + return _inner.err; + } + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultRDCleanPathResultTypeBoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultRDCleanPathResultTypeBoxIronRdpError.cs new file mode 100644 index 00000000..6a9c6e8a --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultRDCleanPathResultTypeBoxIronRdpError.cs @@ -0,0 +1,46 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct RdcleanpathFfiResultRDCleanPathResultTypeBoxIronRdpError +{ + [StructLayout(LayoutKind.Explicit)] + private unsafe struct InnerUnion + { + [FieldOffset(0)] + internal RDCleanPathResultType ok; + [FieldOffset(0)] + internal IronRdpError* err; + } + + private InnerUnion _inner; + + [MarshalAs(UnmanagedType.U1)] + public bool isOk; + + public unsafe RDCleanPathResultType Ok + { + get + { + return _inner.ok; + } + } + + public unsafe IronRdpError* Err + { + get + { + return _inner.err; + } + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultU16BoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultU16BoxIronRdpError.cs new file mode 100644 index 00000000..3eaf3200 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultU16BoxIronRdpError.cs @@ -0,0 +1,46 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct RdcleanpathFfiResultU16BoxIronRdpError +{ + [StructLayout(LayoutKind.Explicit)] + private unsafe struct InnerUnion + { + [FieldOffset(0)] + internal ushort ok; + [FieldOffset(0)] + internal IronRdpError* err; + } + + private InnerUnion _inner; + + [MarshalAs(UnmanagedType.U1)] + public bool isOk; + + public unsafe ushort Ok + { + get + { + return _inner.ok; + } + } + + public unsafe IronRdpError* Err + { + get + { + return _inner.err; + } + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultUsizeBoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultUsizeBoxIronRdpError.cs new file mode 100644 index 00000000..eecbdaea --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawRdcleanpathFfiResultUsizeBoxIronRdpError.cs @@ -0,0 +1,46 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct RdcleanpathFfiResultUsizeBoxIronRdpError +{ + [StructLayout(LayoutKind.Explicit)] + private unsafe struct InnerUnion + { + [FieldOffset(0)] + internal nuint ok; + [FieldOffset(0)] + internal IronRdpError* err; + } + + private InnerUnion _inner; + + [MarshalAs(UnmanagedType.U1)] + public bool isOk; + + public unsafe nuint Ok + { + get + { + return _inner.ok; + } + } + + public unsafe IronRdpError* Err + { + get + { + return _inner.err; + } + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs b/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs index 0a03fd6e..497e70ab 100644 --- a/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs +++ b/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs @@ -11,28 +11,17 @@ public static class Connection { var client = await CreateTcpConnection(serverName, port); string clientAddr = client.Client.LocalEndPoint.ToString(); - Console.WriteLine(clientAddr); + System.Diagnostics.Debug.WriteLine(clientAddr); var framed = new Framed(client.GetStream()); var connector = ClientConnector.New(config, clientAddr); - connector.WithDynamicChannelDisplayControl(); - var dvcPipeProxy = config.DvcPipeProxy; - if (dvcPipeProxy != null) - { - connector.WithDynamicChannelPipeProxy(dvcPipeProxy); - } - - if (factory != null) - { - var cliprdr = factory.BuildCliprdr(); - connector.AttachStaticCliprdr(cliprdr); - } + ConnectionHelpers.SetupConnector(connector, config, factory); await ConnectBegin(framed, connector); var (serverPublicKey, framedSsl) = await SecurityUpgrade(framed, connector); - var result = await ConnectFinalize(serverName, connector, serverPublicKey, framedSsl); + var result = await ConnectionHelpers.ConnectFinalize(serverName, connector, serverPublicKey, framedSsl); return (result, framedSsl); } @@ -67,78 +56,11 @@ public static class Connection } } - - private static async Task ConnectFinalize(string serverName, ClientConnector connector, - byte[] serverPubKey, Framed framedSsl) - { - var writeBuf2 = WriteBuf.New(); - if (connector.ShouldPerformCredssp()) - { - await PerformCredsspSteps(connector, serverName, writeBuf2, framedSsl, serverPubKey); - } - - while (!connector.GetDynState().IsTerminal()) - { - await SingleSequenceStep(connector, writeBuf2, framedSsl); - } - - ClientConnectorState state = connector.ConsumeAndCastToClientConnectorState(); - - if (state.GetEnumType() == ClientConnectorStateType.Connected) - { - return state.GetConnectedResult(); - } - else - { - throw new IronRdpLibException(IronRdpLibExceptionType.ConnectionFailed, "Connection failed"); - } - } - - private static async Task PerformCredsspSteps(ClientConnector connector, string serverName, WriteBuf writeBuf, - Framed framedSsl, byte[] serverpubkey) - { - var credsspSequenceInitResult = CredsspSequence.Init(connector, serverName, serverpubkey, null); - var credsspSequence = credsspSequenceInitResult.GetCredsspSequence(); - var tsRequest = credsspSequenceInitResult.GetTsRequest(); - var tcpClient = new TcpClient(); - while (true) - { - var generator = credsspSequence.ProcessTsRequest(tsRequest); - var clientState = await ResolveGenerator(generator, tcpClient); - writeBuf.Clear(); - var written = credsspSequence.HandleProcessResult(clientState, writeBuf); - - if (written.GetSize().IsSome()) - { - var actualSize = (int)written.GetSize().Get(); - var response = new byte[actualSize]; - writeBuf.ReadIntoBuf(response); - await framedSsl.Write(response); - } - - var pduHint = credsspSequence.NextPduHint(); - if (pduHint == null) - { - break; - } - - var pdu = await framedSsl.ReadByHint(pduHint); - var decoded = credsspSequence.DecodeServerMessage(pdu); - - // Don't remove, DecodeServerMessage is generated, and it can return null - if (null == decoded) - { - break; - } - - tsRequest = decoded; - } - } - - private static async Task ResolveGenerator(CredsspProcessGenerator generator, TcpClient tcpClient) + internal static async Task ResolveGenerator(CredsspProcessGenerator generator, TcpClient tcpClient) { var state = generator.Start(); NetworkStream? stream = null; + while (true) { if (state.IsSuspended()) @@ -147,16 +69,17 @@ public static class Connection var protocol = request.GetProtocol(); var url = request.GetUrl(); var data = request.GetData(); - if (null == stream) - { - url = url.Replace("tcp://", ""); - var split = url.Split(":"); - await tcpClient.ConnectAsync(split[0], int.Parse(split[1])); - stream = tcpClient.GetStream(); - } if (protocol == NetworkRequestProtocol.Tcp) { + if (null == stream) + { + url = url.Replace("tcp://", ""); + var split = url.Split(":"); + await tcpClient.ConnectAsync(split[0], int.Parse(split[1])); + stream = tcpClient.GetStream(); + } + stream.Write(Utils.VecU8ToByte(data)); var readBuf = new byte[8096]; var readlen = await stream.ReadAsync(readBuf, 0, readBuf.Length); @@ -166,13 +89,29 @@ public static class Connection } else { - throw new Exception("Unimplemented protocol"); + throw new Exception($"Unimplemented protocol: {protocol}"); + } + } + else if (state.IsCompleted()) + { + try + { + var clientState = state.GetClientStateIfCompleted(); + return clientState; + } + catch (IronRdpException ex) + { + System.Diagnostics.Debug.WriteLine($"[ResolveGenerator] Error getting client state: {ex.Message}"); + System.Diagnostics.Debug.WriteLine($"[ResolveGenerator] Error kind: {ex.Inner?.Kind}"); + System.Diagnostics.Debug.WriteLine($"[ResolveGenerator] Stack trace: {ex.StackTrace}"); + throw; } } else { - var clientState = state.GetClientStateIfCompleted(); - return clientState; + var errorMsg = $"[ResolveGenerator] Generator state is neither suspended nor completed. IsSuspended={state.IsSuspended()}, IsCompleted={state.IsCompleted()}"; + System.Diagnostics.Debug.WriteLine(errorMsg); + throw new InvalidOperationException(errorMsg); } } } diff --git a/ffi/dotnet/Devolutions.IronRdp/src/ConnectionHelpers.cs b/ffi/dotnet/Devolutions.IronRdp/src/ConnectionHelpers.cs new file mode 100644 index 00000000..d57aaaca --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/src/ConnectionHelpers.cs @@ -0,0 +1,121 @@ +using System.Net.Sockets; + +namespace Devolutions.IronRdp; + +/// +/// Internal helper class providing shared connection logic for both direct and RDCleanPath connections. +/// +internal static class ConnectionHelpers +{ + /// + /// Sets up common connector configuration including dynamic channels and clipboard. + /// + internal static void SetupConnector(ClientConnector connector, Config config, CliprdrBackendFactory? factory) + { + connector.WithDynamicChannelDisplayControl(); + + var dvcPipeProxy = config.DvcPipeProxy; + if (dvcPipeProxy != null) + { + connector.WithDynamicChannelPipeProxy(dvcPipeProxy); + } + + if (factory != null) + { + var cliprdr = factory.BuildCliprdr(); + connector.AttachStaticCliprdr(cliprdr); + } + } + + /// + /// Performs CredSSP authentication steps over any stream type. + /// + internal static async Task PerformCredsspSteps( + ClientConnector connector, + string serverName, + WriteBuf writeBuf, + Framed framed, + byte[] serverpubkey) where T : Stream + { + // Extract hostname from "hostname:port" format if needed + // CredSSP needs just the hostname for the service principal name (TERMSRV/hostname) + var hostname = serverName; + var colonIndex = serverName.IndexOf(':'); + if (colonIndex > 0) + { + hostname = serverName.Substring(0, colonIndex); + } + + var credsspSequenceInitResult = CredsspSequence.Init(connector, hostname, serverpubkey, null); + var credsspSequence = credsspSequenceInitResult.GetCredsspSequence(); + var tsRequest = credsspSequenceInitResult.GetTsRequest(); + var tcpClient = new TcpClient(); + + while (true) + { + var generator = credsspSequence.ProcessTsRequest(tsRequest); + var clientState = await Connection.ResolveGenerator(generator, tcpClient); + writeBuf.Clear(); + var written = credsspSequence.HandleProcessResult(clientState, writeBuf); + + if (written.GetSize().IsSome()) + { + var actualSize = (int)written.GetSize().Get(); + var response = new byte[actualSize]; + writeBuf.ReadIntoBuf(response); + await framed.Write(response); + } + + var pduHint = credsspSequence.NextPduHint(); + if (pduHint == null) + { + break; + } + + var pdu = await framed.ReadByHint(pduHint); + var decoded = credsspSequence.DecodeServerMessage(pdu); + + // Don't remove, DecodeServerMessage is generated, and it can return null + if (null == decoded) + { + break; + } + + tsRequest = decoded; + } + } + + /// + /// Finalizes the RDP connection after security upgrade, performing CredSSP if needed + /// and completing the connection sequence. + /// + internal static async Task ConnectFinalize( + string serverName, + ClientConnector connector, + byte[] serverPubKey, + Framed framedSsl) where T : Stream + { + var writeBuf = WriteBuf.New(); + + if (connector.ShouldPerformCredssp()) + { + await PerformCredsspSteps(connector, serverName, writeBuf, framedSsl, serverPubKey); + } + + while (!connector.GetDynState().IsTerminal()) + { + await Connection.SingleSequenceStep(connector, writeBuf, framedSsl); + } + + ClientConnectorState state = connector.ConsumeAndCastToClientConnectorState(); + + if (state.GetEnumType() == ClientConnectorStateType.Connected) + { + return state.GetConnectedResult(); + } + else + { + throw new IronRdpLibException(IronRdpLibExceptionType.ConnectionFailed, "Connection failed"); + } + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/src/Framed.cs b/ffi/dotnet/Devolutions.IronRdp/src/Framed.cs index f03530f3..2e8772e7 100644 --- a/ffi/dotnet/Devolutions.IronRdp/src/Framed.cs +++ b/ffi/dotnet/Devolutions.IronRdp/src/Framed.cs @@ -134,4 +134,45 @@ public class Framed where TS : Stream } } } + + /// + /// Reads data from the buffer based on a custom PDU hint function. + /// + /// A custom hint object implementing IPduHint interface. + /// An asynchronous task that represents the operation. The task result contains the read data as a byte array. + public async Task ReadByHint(IPduHint customHint) + { + while (true) + { + var result = customHint.FindSize(this._buffer.ToArray()); + if (result.HasValue) + { + return await this.ReadExact((nuint)result.Value.Item2); + } + else + { + var len = await this.Read(); + if (len == 0) + { + throw new Exception("EOF"); + } + } + } + } +} + +/// +/// Interface for custom PDU hint implementations. +/// +public interface IPduHint +{ + /// + /// Finds the size of a PDU in the given byte array. + /// + /// The byte array to analyze. + /// + /// A tuple (detected, size) if PDU is detected, null if more bytes are needed. + /// Throws exception if invalid PDU is detected. + /// + (bool, int)? FindSize(byte[] bytes); } \ No newline at end of file diff --git a/ffi/dotnet/Devolutions.IronRdp/src/RDCleanPathConnection.cs b/ffi/dotnet/Devolutions.IronRdp/src/RDCleanPathConnection.cs new file mode 100644 index 00000000..725c0525 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/src/RDCleanPathConnection.cs @@ -0,0 +1,200 @@ +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +namespace Devolutions.IronRdp; + +/// +/// Provides methods for connecting to RDP servers through an RDCleanPath-compatible gateway +/// (such as Devolutions Gateway or Cloudflare) using WebSocket. +/// +public static class RDCleanPathConnection +{ + /// + /// Connects to an RDP server through an RDCleanPath-compatible gateway using WebSocket. + /// + /// The RDP connection configuration + /// The WebSocket URL to the RDCleanPath gateway (e.g., "ws://localhost:7171/jet/rdp") + /// The JWT authentication token for the RDCleanPath gateway + /// The destination RDP server address (e.g., "10.10.0.3:3389") + /// Optional preconnection blob for Hyper-V VM connections + /// Optional clipboard backend factory + /// A tuple containing the connection result and framed WebSocket stream + public static async Task<(ConnectionResult, Framed)> ConnectRDCleanPath( + Config config, + string gatewayUrl, + string authToken, + string destination, + string? pcb = null, + CliprdrBackendFactory? factory = null) + { + // Step 1: Connect WebSocket to gateway + System.Diagnostics.Debug.WriteLine($"Connecting to gateway at {gatewayUrl}..."); + var ws = await WebSocketStream.ConnectAsync(new Uri(gatewayUrl)); + var framed = new Framed(ws); + + // Step 2: Get client local address from the WebSocket connection + // This mimics Rust: let client_addr = socket.local_addr()?; + string clientAddr = ws.ClientAddr; + System.Diagnostics.Debug.WriteLine($"Client local address: {clientAddr}"); + + // Step 3: Setup ClientConnector + var connector = ClientConnector.New(config, clientAddr); + ConnectionHelpers.SetupConnector(connector, config, factory); + + // Step 4: Perform RDCleanPath handshake + System.Diagnostics.Debug.WriteLine("Performing RDCleanPath handshake..."); + var (serverPublicKey, framedAfterHandshake) = await ConnectRdCleanPath( + framed, connector, destination, authToken, pcb ?? ""); + + // Step 5: Mark security upgrade as done (WebSocket already has TLS) + connector.MarkSecurityUpgradeAsDone(); + + // Step 6: Finalize connection + System.Diagnostics.Debug.WriteLine("Finalizing RDP connection..."); + var result = await ConnectionHelpers.ConnectFinalize(destination, connector, serverPublicKey, framedAfterHandshake); + + System.Diagnostics.Debug.WriteLine("Gateway connection established successfully!"); + return (result, framedAfterHandshake); + } + + /// + /// Performs the RDCleanPath handshake with the RDCleanPath-compatible gateway. + /// + private static async Task<(byte[], Framed)> ConnectRdCleanPath( + Framed framed, + ClientConnector connector, + string destination, + string authToken, + string pcb) + { + var writeBuf = WriteBuf.New(); + + // Step 1: Generate X.224 Connection Request + System.Diagnostics.Debug.WriteLine("Generating X.224 Connection Request..."); + var written = connector.StepNoInput(writeBuf); + var x224PduSize = (int)written.GetSize().Get(); + var x224Pdu = new byte[x224PduSize]; + writeBuf.ReadIntoBuf(x224Pdu); + + // Step 2: Create and send RDCleanPath Request + System.Diagnostics.Debug.WriteLine($"Sending RDCleanPath request to {destination}..."); + var rdCleanPathReq = RDCleanPathPdu.NewRequest(x224Pdu, destination, authToken, pcb); + var reqBytes = rdCleanPathReq.ToDer(); + var reqBytesArray = new byte[reqBytes.GetSize()]; + reqBytes.Fill(reqBytesArray); + await framed.Write(reqBytesArray); + + // Step 3: Read RDCleanPath Response + System.Diagnostics.Debug.WriteLine("Waiting for RDCleanPath response..."); + var respBytes = await framed.ReadByHint(new RDCleanPathHint()); + var rdCleanPathResp = RDCleanPathPdu.FromDer(respBytes); + + // Step 4: Determine response type and handle accordingly + var resultType = rdCleanPathResp.GetType(); + + if (resultType == RDCleanPathResultType.Response) + { + System.Diagnostics.Debug.WriteLine("RDCleanPath handshake successful!"); + + // Extract X.224 response + var x224Response = rdCleanPathResp.GetX224Response(); + var x224ResponseBytes = new byte[x224Response.GetSize()]; + x224Response.Fill(x224ResponseBytes); + + // Process X.224 response with connector + writeBuf.Clear(); + connector.Step(x224ResponseBytes, writeBuf); + + // Extract server public key from certificate chain + var certChain = rdCleanPathResp.GetServerCertChain(); + if (certChain.IsEmpty()) + { + throw new IronRdpLibException( + IronRdpLibExceptionType.ConnectionFailed, + "Server certificate chain is empty"); + } + + var firstCert = certChain.Next(); + if (firstCert == null) + { + throw new IronRdpLibException( + IronRdpLibExceptionType.ConnectionFailed, + "Failed to get first certificate from chain"); + } + + var certBytes = new byte[firstCert.GetSize()]; + firstCert.Fill(certBytes); + + var serverPublicKey = ExtractPublicKeyFromX509(certBytes); + + System.Diagnostics.Debug.WriteLine($"Extracted server public key (length: {serverPublicKey.Length})"); + + return (serverPublicKey, framed); + } + else if (resultType == RDCleanPathResultType.GeneralError) + { + var errorCode = rdCleanPathResp.GetErrorCode(); + var errorMessage = rdCleanPathResp.GetErrorMessage(); + throw new IronRdpLibException( + IronRdpLibExceptionType.ConnectionFailed, + $"RDCleanPath error (code {errorCode}): {errorMessage}"); + } + else if (resultType == RDCleanPathResultType.NegotiationError) + { + throw new IronRdpLibException( + IronRdpLibExceptionType.ConnectionFailed, + "RDCleanPath negotiation error: Server rejected connection parameters"); + } + else + { + throw new IronRdpLibException( + IronRdpLibExceptionType.ConnectionFailed, + $"Unexpected RDCleanPath response type: {resultType}"); + } + } + + /// + /// Extracts the public key from an X.509 certificate in DER format. + /// + private static byte[] ExtractPublicKeyFromX509(byte[] certDer) + { + try + { + var cert = new X509Certificate2(certDer); + return cert.GetPublicKey(); + } + catch (Exception ex) + { + throw new IronRdpLibException( + IronRdpLibExceptionType.ConnectionFailed, + $"Failed to extract public key from certificate: {ex.Message}"); + } + } +} + +/// +/// PDU hint for detecting RDCleanPath PDUs in the stream. +/// +public class RDCleanPathHint : IPduHint +{ + public (bool, int)? FindSize(byte[] bytes) + { + var detection = RDCleanPathPdu.Detect(bytes); + + if (detection.IsDetected()) + { + var totalLength = (int)detection.GetTotalLength(); + return (true, totalLength); + } + + if (detection.IsNotEnoughBytes()) + { + return null; // Need more bytes + } + + // Detection failed + throw new IronRdpLibException( + IronRdpLibExceptionType.ConnectionFailed, + "Invalid RDCleanPath PDU detected"); + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/src/WebsocketStream.cs b/ffi/dotnet/Devolutions.IronRdp/src/WebsocketStream.cs new file mode 100644 index 00000000..5f528101 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/src/WebsocketStream.cs @@ -0,0 +1,212 @@ +using System; +using System.Buffers; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Net.Sockets; +using System.Net.WebSockets; +using System.Threading; +using System.Threading.Tasks; + +public sealed class WebSocketStream : Stream +{ + private readonly ClientWebSocket _ws; + private readonly byte[] _recvBuf; + private int _recvPos; + private int _recvLen; + private bool _remoteClosed; + private bool _disposed; + private readonly string _clientAddr; + + private const int DefaultRecvBufferSize = 64 * 1024; + private const int MaxSendFrame = 16 * 1024; // send in chunks + + private WebSocketStream(ClientWebSocket ws, int receiveBufferSize, string clientAddr) + { + _ws = ws ?? throw new ArgumentNullException(nameof(ws)); + _recvBuf = ArrayPool.Shared.Rent(Math.Max(1024, receiveBufferSize)); + _clientAddr = clientAddr; + } + + public static async Task ConnectAsync( + Uri uri, + ClientWebSocket? ws = null, + int receiveBufferSize = DefaultRecvBufferSize, + CancellationToken ct = default) + { + // Capture the local endpoint from the socket using SocketsHttpHandler.ConnectCallback + // This follows the Rust approach: socket.local_addr() + IPEndPoint? localEndPoint = null; + + var handler = new SocketsHttpHandler + { + ConnectCallback = async (context, cancellationToken) => + { + var socket = new Socket(SocketType.Stream, ProtocolType.Tcp); + + // Set TCP_NODELAY (matching Rust: socket.set_nodelay(true)) + socket.NoDelay = true; + + // Connect to the endpoint + await socket.ConnectAsync(context.DnsEndPoint, cancellationToken).ConfigureAwait(false); + + // Capture the local endpoint after connection + localEndPoint = socket.LocalEndPoint as IPEndPoint; + + return new NetworkStream(socket, ownsSocket: true); + } + }; + + var invoker = new HttpMessageInvoker(handler); + + ws ??= new ClientWebSocket(); + await ws.ConnectAsync(uri, invoker, ct).ConfigureAwait(false); + + string clientAddr = localEndPoint?.ToString() ?? "127.0.0.1:0"; + + return new WebSocketStream(ws, receiveBufferSize, clientAddr); + } + + public ClientWebSocket Socket => _ws; + + /// + /// Gets the local client address in "IP:port" format. + /// This is the address that was determined when establishing the TCP connection. + /// + public string ClientAddr => _clientAddr; + + public override bool CanRead => true; + public override bool CanSeek => false; + public override bool CanWrite => true; + public override long Length => throw new NotSupportedException(); + public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } + + public override void Flush() { /* no-op */ } + public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask; + + public override int Read(byte[] buffer, int offset, int count) => + ReadAsync(buffer.AsMemory(offset, count)).AsTask().GetAwaiter().GetResult(); + + public override void Write(byte[] buffer, int offset, int count) => + WriteAsync(buffer.AsMemory(offset, count)).GetAwaiter().GetResult(); + + public override async ValueTask ReadAsync( + Memory destination, CancellationToken cancellationToken = default) + { + if (_disposed) throw new ObjectDisposedException(nameof(WebSocketStream)); + if (_remoteClosed) return 0; + if (destination.Length == 0) return 0; + + // Fill local buffer if empty + if (_recvLen == 0) + { + var mem = _recvBuf.AsMemory(); + while (true) + { + var result = await _ws.ReceiveAsync(mem, cancellationToken).ConfigureAwait(false); + + // Close frame → signal EOF + if (result.MessageType == WebSocketMessageType.Close) + { + _remoteClosed = true; + try { await _ws.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "OK", cancellationToken).ConfigureAwait(false); } + catch { /* ignore */ } + return 0; + } + + if (result.MessageType == WebSocketMessageType.Text) + throw new InvalidOperationException("Received TEXT frame; this stream expects BINARY."); + + // Some data arrived + if (result.Count > 0) + { + _recvPos = 0; + _recvLen = result.Count; + break; + } + + // Keep looping if Count == 0 (can happen with pings/keepers) + } + } + + var toCopy = Math.Min(destination.Length, _recvLen); + new ReadOnlySpan(_recvBuf, _recvPos, toCopy).CopyTo(destination.Span); + _recvPos += toCopy; + _recvLen -= toCopy; + + // If we've drained local buffer, try to prefetch next chunk (non-blocking behavior not guaranteed) + if (_recvLen == 0 && _ws.State == WebSocketState.Open) + { + // optional prefetch: not strictly necessary—kept simple + } + + return toCopy; + } + + public override async Task WriteAsync( + byte[] buffer, int offset, int count, CancellationToken cancellationToken) + => await WriteAsync(buffer.AsMemory(offset, count), cancellationToken); + + public override async ValueTask WriteAsync( + ReadOnlyMemory source, CancellationToken cancellationToken = default) + { + if (_disposed) throw new ObjectDisposedException(nameof(WebSocketStream)); + if (_ws.State != WebSocketState.Open) throw new IOException("WebSocket is not open."); + + // Treat each Write* as one complete WebSocket message (Binary). + // Chunk large payloads as continuation frames and set EndOfMessage on the last chunk. + int sent = 0; + while (sent < source.Length) + { + var chunkLen = Math.Min(MaxSendFrame, source.Length - sent); + var chunk = source.Slice(sent, chunkLen); + sent += chunkLen; + + bool end = (sent == source.Length); + await _ws.SendAsync(chunk, WebSocketMessageType.Binary, end, cancellationToken).ConfigureAwait(false); + } + } + + public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); + public override void SetLength(long value) => throw new NotSupportedException(); + + protected override void Dispose(bool disposing) + { + if (_disposed) return; + if (disposing) + { + try + { + if (_ws.State == WebSocketState.Open) + { + _ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Disposing", CancellationToken.None) + .GetAwaiter().GetResult(); + } + } + catch { /* ignore on dispose */ } + _ws.Dispose(); + ArrayPool.Shared.Return(_recvBuf); + } + _disposed = true; + base.Dispose(disposing); + } + +#if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER + public override async ValueTask DisposeAsync() + { + if (!_disposed) + { + try + { + if (_ws.State == WebSocketState.Open) + await _ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Disposing", CancellationToken.None).ConfigureAwait(false); + } + catch { /* ignore */ } + _ws.Dispose(); + ArrayPool.Shared.Return(_recvBuf); + _disposed = true; + } + await base.DisposeAsync().ConfigureAwait(false); + } +#endif +} diff --git a/ffi/src/clipboard/mod.rs b/ffi/src/clipboard/mod.rs index e50aa1fe..386e7b85 100644 --- a/ffi/src/clipboard/mod.rs +++ b/ffi/src/clipboard/mod.rs @@ -34,8 +34,8 @@ pub struct FfiClipbarodMessageProxy { impl ironrdp::cliprdr::backend::ClipboardMessageProxy for FfiClipbarodMessageProxy { fn send_clipboard_message(&self, message: ironrdp::cliprdr::backend::ClipboardMessage) { - if let Err(err) = self.sender.send(message) { - error!("Failed to send clipboard message: {:?}", err); + if let Err(error) = self.sender.send(message) { + error!(?error, "Failed to send clipboard message"); } } } diff --git a/ffi/src/connector/mod.rs b/ffi/src/connector/mod.rs index e9226467..332177af 100644 --- a/ffi/src/connector/mod.rs +++ b/ffi/src/connector/mod.rs @@ -97,7 +97,7 @@ pub mod ffi { pub fn with_dynamic_channel_display_control(&mut self) -> Result<(), Box> { self.with_dvc(DisplayControlClient::new(|c| { - info!(DisplayCountrolCapabilities = ?c, "DisplayControl capabilities received"); + info!(display_control_capabilities = ?c, "DisplayControl capabilities received"); Ok(Vec::new()) })) } @@ -185,7 +185,7 @@ pub mod ffi { let Some(connector) = self.0.as_ref() else { return Err(ValueConsumedError::for_item("connector").into()); }; - tracing::trace!(pduhint=?connector.next_pdu_hint(), "Reading next PDU hint"); + tracing::trace!(pdu_hint=?connector.next_pdu_hint(), "Reading next PDU hint"); Ok(connector.next_pdu_hint().map(PduHint).map(Box::new)) } diff --git a/ffi/src/error.rs b/ffi/src/error.rs index cf32007d..ceaa4e4e 100644 --- a/ffi/src/error.rs +++ b/ffi/src/error.rs @@ -6,6 +6,7 @@ use ironrdp::connector::ConnectorError; use ironrdp::session::SessionError; #[cfg(target_os = "windows")] use ironrdp_cliprdr_native::WinCliprdrError; +use ironrdp_rdcleanpath::der; use self::ffi::IronRdpErrorKind; @@ -93,6 +94,32 @@ impl From for IronRdpErrorKind { } } +impl From for IronRdpErrorKind { + fn from(_val: der::Error) -> Self { + IronRdpErrorKind::DecodeError + } +} + +impl From for IronRdpErrorKind { + fn from(_val: ironrdp_rdcleanpath::MissingRDCleanPathField) -> Self { + IronRdpErrorKind::Generic + } +} + +pub struct GenericError(pub anyhow::Error); + +impl Display for GenericError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#}", self.0) + } +} + +impl From for IronRdpErrorKind { + fn from(_val: GenericError) -> Self { + IronRdpErrorKind::Generic + } +} + impl From for Box where T: Into + ToString, @@ -221,11 +248,7 @@ impl IncorrectEnumTypeErrorBuilder { impl Display for IncorrectEnumTypeError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!( - f, - "expected enum variable {}, of enum {}", - self.expected, self.enum_name - ) + write!(f, "expected enum variable {} of enum {}", self.expected, self.enum_name) } } diff --git a/ffi/src/lib.rs b/ffi/src/lib.rs index 407847ce..44009cd5 100644 --- a/ffi/src/lib.rs +++ b/ffi/src/lib.rs @@ -11,6 +11,7 @@ pub mod graphics; pub mod input; pub mod log; pub mod pdu; +pub mod rdcleanpath; pub mod session; pub mod svc; pub mod utils; diff --git a/ffi/src/log.rs b/ffi/src/log.rs index 7f7750a2..f050b150 100644 --- a/ffi/src/log.rs +++ b/ffi/src/log.rs @@ -23,7 +23,7 @@ pub mod ffi { INIT_LOG.call_once(|| { let log_file = std::env::var(IRONRDP_LOG_PATH).ok(); let log_file = log_file.as_deref(); - setup_logging(log_file).expect("Failed to setup logging"); + setup_logging(log_file).expect("failed to setup logging"); }); } } diff --git a/ffi/src/rdcleanpath.rs b/ffi/src/rdcleanpath.rs new file mode 100644 index 00000000..a842c747 --- /dev/null +++ b/ffi/src/rdcleanpath.rs @@ -0,0 +1,268 @@ +#[diplomat::bridge] +pub mod ffi { + use core::fmt::Write as _; + + use anyhow::Context as _; + use diplomat_runtime::DiplomatWriteable; + + use crate::error::ffi::IronRdpError; + use crate::error::GenericError; + use crate::utils::ffi::VecU8; + + #[diplomat::opaque] + pub struct RDCleanPathPdu(pub ironrdp_rdcleanpath::RDCleanPathPdu); + + impl RDCleanPathPdu { + /// Creates a new RDCleanPath request PDU + /// + /// # Arguments + /// * `x224_pdu` - The X.224 Connection Request PDU bytes + /// * `destination` - The destination RDP server address (e.g., "10.10.0.3:3389") + /// * `proxy_auth` - The JWT authentication token + /// * `pcb` - Optional preconnection blob (for Hyper-V VM connections, empty string if not needed) + pub fn new_request( + x224_pdu: &[u8], + destination: &str, + proxy_auth: &str, + pcb: &str, + ) -> Result, Box> { + let pcb_opt = if pcb.is_empty() { None } else { Some(pcb.to_owned()) }; + + let pdu = ironrdp_rdcleanpath::RDCleanPathPdu::new_request( + x224_pdu.to_vec(), + destination.to_owned(), + proxy_auth.to_owned(), + pcb_opt, + ) + .context("failed to create RDCleanPath request") + .map_err(GenericError)?; + + Ok(Box::new(RDCleanPathPdu(pdu))) + } + + /// Decodes a RDCleanPath PDU from DER-encoded bytes + pub fn from_der(bytes: &[u8]) -> Result, Box> { + let pdu = ironrdp_rdcleanpath::RDCleanPathPdu::from_der(bytes) + .context("failed to decode RDCleanPath PDU") + .map_err(GenericError)?; + + Ok(Box::new(RDCleanPathPdu(pdu))) + } + + /// Encodes the RDCleanPath PDU to DER-encoded bytes + pub fn to_der(&self) -> Result, Box> { + let bytes = self + .0 + .to_der() + .context("failed to encode RDCleanPath PDU") + .map_err(GenericError)?; + + Ok(Box::new(VecU8(bytes))) + } + + /// Detects if the bytes contain a valid RDCleanPath PDU and returns detection result + pub fn detect(bytes: &[u8]) -> Box { + let result = ironrdp_rdcleanpath::RDCleanPathPdu::detect(bytes); + Box::new(RDCleanPathDetectionResult(result)) + } + + /// Gets the type of this RDCleanPath PDU + pub fn get_type(&self) -> Result> { + if self.0.destination.is_some() { + if self.0.proxy_auth.is_none() { + return Err(Self::missing_field("proxy_auth")); + } + + if self.0.x224_connection_pdu.is_none() { + return Err(Self::missing_field("x224_connection_pdu")); + } + + Ok(RDCleanPathResultType::Request) + } else if self.0.server_addr.is_some() { + if self.0.x224_connection_pdu.is_none() { + return Err(Self::missing_field("x224_connection_pdu")); + } + + if self.0.server_cert_chain.is_none() { + return Err(Self::missing_field("server_cert_chain")); + } + + Ok(RDCleanPathResultType::Response) + } else if let Some(error) = &self.0.error { + if error.error_code == ironrdp_rdcleanpath::NEGOTIATION_ERROR_CODE { + if self.0.x224_connection_pdu.is_none() { + return Err(Self::missing_field("x224_connection_pdu")); + } + + Ok(RDCleanPathResultType::NegotiationError) + } else { + Ok(RDCleanPathResultType::GeneralError) + } + } else { + Err(Self::missing_field("error")) + } + } + + /// Gets the X.224 connection response bytes (for Response or NegotiationError variants) + pub fn get_x224_response(&self) -> Result, Box> { + if self.0.server_addr.is_some() { + let x224 = self + .0 + .x224_connection_pdu + .as_ref() + .ok_or_else(|| Self::missing_field("x224_connection_pdu"))?; + self.0 + .server_cert_chain + .as_ref() + .ok_or_else(|| Self::missing_field("server_cert_chain"))?; + + Ok(Box::new(VecU8(x224.as_bytes().to_vec()))) + } else if let Some(error) = &self.0.error { + if error.error_code == ironrdp_rdcleanpath::NEGOTIATION_ERROR_CODE { + let x224 = self + .0 + .x224_connection_pdu + .as_ref() + .ok_or_else(|| Self::missing_field("x224_connection_pdu"))?; + + Ok(Box::new(VecU8(x224.as_bytes().to_vec()))) + } else { + Err(GenericError(anyhow::anyhow!("RDCleanPath variant does not contain X.224 response")).into()) + } + } else { + Err(GenericError(anyhow::anyhow!("RDCleanPath variant does not contain X.224 response")).into()) + } + } + + /// Gets the server certificate chain (for Response variant) + /// Returns a vector iterator of certificate bytes + pub fn get_server_cert_chain(&self) -> Result, Box> { + if self.0.server_addr.is_some() { + self.0 + .x224_connection_pdu + .as_ref() + .ok_or_else(|| Self::missing_field("x224_connection_pdu"))?; + let certs = self + .0 + .server_cert_chain + .as_ref() + .ok_or_else(|| Self::missing_field("server_cert_chain"))?; + + let certs: Vec> = certs.iter().map(|cert| cert.as_bytes().to_vec()).collect(); + Ok(Box::new(CertificateChainIterator { certs, index: 0 })) + } else { + Err(GenericError(anyhow::anyhow!( + "RDCleanPath variant does not contain certificate chain" + )) + .into()) + } + } + + /// Gets the server address string (for Response variant) + pub fn get_server_addr<'a>(&'a self, writeable: &'a mut DiplomatWriteable) { + if self.0.server_addr.is_some() + && self.0.server_cert_chain.is_some() + && self.0.x224_connection_pdu.is_some() + { + if let Some(server_addr) = &self.0.server_addr { + let _ = write!(writeable, "{server_addr}"); + } + } + } + + /// Gets error message (for GeneralError variant) + pub fn get_error_message<'a>(&'a self, writeable: &'a mut DiplomatWriteable) { + if let Ok(err) = self.general_error() { + let _ = write!(writeable, "{err}"); + } + } + + /// Gets the error code (for GeneralError variant) + pub fn get_error_code(&self) -> Result> { + let err = self.general_error()?; + Ok(err.error_code) + } + + /// Gets the HTTP status code if present (for GeneralError variant) + /// Returns error if not present or not a GeneralError variant + pub fn get_http_status_code(&self) -> Result> { + let err = self.general_error()?; + + err.http_status_code + .ok_or_else(|| GenericError(anyhow::anyhow!("HTTP status code not present")).into()) + } + + fn missing_field(field: &'static str) -> Box { + GenericError(anyhow::anyhow!("RDCleanPath is missing {field} field")).into() + } + + fn general_error(&self) -> Result<&ironrdp_rdcleanpath::RDCleanPathErr, Box> { + let error = self.0.error.as_ref().ok_or_else(|| Self::missing_field("error"))?; + + if error.error_code == ironrdp_rdcleanpath::NEGOTIATION_ERROR_CODE { + Err(GenericError(anyhow::anyhow!("not a GeneralError variant")).into()) + } else { + Ok(error) + } + } + } + + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum RDCleanPathResultType { + Request, + Response, + GeneralError, + NegotiationError, + } + + #[diplomat::opaque] + pub struct RDCleanPathDetectionResult(pub ironrdp_rdcleanpath::DetectionResult); + + impl RDCleanPathDetectionResult { + pub fn is_detected(&self) -> bool { + matches!(self.0, ironrdp_rdcleanpath::DetectionResult::Detected { .. }) + } + + pub fn is_not_enough_bytes(&self) -> bool { + matches!(self.0, ironrdp_rdcleanpath::DetectionResult::NotEnoughBytes) + } + + pub fn is_failed(&self) -> bool { + matches!(self.0, ironrdp_rdcleanpath::DetectionResult::Failed) + } + + pub fn get_total_length(&self) -> Result> { + if let ironrdp_rdcleanpath::DetectionResult::Detected { total_length, .. } = self.0 { + Ok(total_length) + } else { + Err(GenericError(anyhow::anyhow!("detection result is not Detected variant")).into()) + } + } + } + + #[diplomat::opaque] + pub struct CertificateChainIterator { + certs: Vec>, + index: usize, + } + + impl CertificateChainIterator { + pub fn next(&mut self) -> Option> { + if self.index < self.certs.len() { + let cert = self.certs[self.index].clone(); + self.index += 1; + Some(Box::new(VecU8(cert))) + } else { + None + } + } + + pub fn len(&self) -> usize { + self.certs.len() + } + + pub fn is_empty(&self) -> bool { + self.certs.is_empty() + } + } +} From 9622619e8c66d325ace7e51a77a1b823cfbc1ba1 Mon Sep 17 00:00:00 2001 From: Alex Yusiuk <55661041+RRRadicalEdward@users.noreply.github.com> Date: Thu, 6 Nov 2025 00:23:22 +0200 Subject: [PATCH 26/51] refactor: add `as_conversions` clippy correctness lint (#1021) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Benoît CORTIER --- Cargo.toml | 2 +- benches/src/perfenc.rs | 5 +- clippy.toml | 2 +- crates/ironrdp-client/src/app.rs | 11 +- crates/ironrdp-client/src/rdp.rs | 4 +- .../src/windows/clipboard_data_ref.rs | 2 +- .../src/windows/clipboard_impl.rs | 14 ++- .../ironrdp-cliprdr-native/src/windows/mod.rs | 10 +- .../src/windows/utils.rs | 2 +- crates/ironrdp-cliprdr/src/lib.rs | 4 - crates/ironrdp-connector/src/connection.rs | 4 +- crates/ironrdp-dvc/src/pdu.rs | 18 ++- crates/ironrdp-fuzzing/src/oracles/mod.rs | 4 +- .../ironrdp-graphics/src/color_conversion.rs | 13 +- crates/ironrdp-graphics/src/dwt.rs | 115 +++++++++++------- crates/ironrdp-graphics/src/lib.rs | 4 - crates/ironrdp-graphics/src/pointer.rs | 9 +- crates/ironrdp-graphics/src/quantization.rs | 4 +- .../src/rdp6/bitmap_stream/decoder.rs | 14 ++- crates/ironrdp-graphics/src/rdp6/rle.rs | 19 +-- crates/ironrdp-graphics/src/rlgr.rs | 95 ++++++++++----- .../src/zgfx/control_messages.rs | 8 +- crates/ironrdp-graphics/src/zgfx/mod.rs | 25 ++-- crates/ironrdp-input/src/lib.rs | 10 +- crates/ironrdp-mstsgu/src/lib.rs | 10 +- crates/ironrdp-mstsgu/src/proto.rs | 72 ++++++++--- .../src/gcc/monitor_extended_data.rs | 8 +- .../rdp/capability_sets/bitmap_codecs/mod.rs | 4 + .../ironrdp-pdu/src/rdp/server_error_info.rs | 4 + .../vc/dvc/gfx/graphics_messages/server.rs | 4 + crates/ironrdp-rdpdr/src/lib.rs | 4 - crates/ironrdp-rdpdr/src/pdu/efs.rs | 36 ++++-- crates/ironrdp-rdpdr/src/pdu/esc/mod.rs | 24 ++-- crates/ironrdp-rdpdr/src/pdu/esc/rpce.rs | 4 + crates/ironrdp-rdpdr/src/pdu/mod.rs | 8 ++ crates/ironrdp-rdpsnd-native/src/cpal.rs | 4 + crates/ironrdp-rdpsnd/src/client.rs | 4 +- crates/ironrdp-rdpsnd/src/pdu/mod.rs | 22 ++-- crates/ironrdp-server/src/encoder/bitmap.rs | 2 +- crates/ironrdp-server/src/encoder/mod.rs | 12 +- crates/ironrdp-server/src/handler.rs | 13 +- crates/ironrdp-session/src/image.rs | 87 ++++++------- crates/ironrdp-session/src/rfx.rs | 7 +- crates/ironrdp-testsuite-core/src/gcc.rs | 12 ++ crates/ironrdp-testsuite-core/src/rdp.rs | 2 +- .../src/security_data.rs | 2 + .../tests/dvc/data_first.rs | 1 + crates/ironrdp-tokio/src/reqwest.rs | 7 +- crates/ironrdp-web/src/canvas.rs | 3 +- crates/ironrdp-web/src/session.rs | 17 ++- crates/ironrdp/examples/server.rs | 10 +- xtask/src/cov.rs | 9 +- 52 files changed, 526 insertions(+), 264 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 058d516d..135c56f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,7 +92,7 @@ fn_to_numeric_cast_any = "warn" ptr_cast_constness = "warn" # == Correctness == # -#as_conversions = "warn" +as_conversions = "warn" cast_lossless = "warn" cast_possible_truncation = "warn" cast_possible_wrap = "warn" diff --git a/benches/src/perfenc.rs b/benches/src/perfenc.rs index fc486460..5abc94f3 100644 --- a/benches/src/perfenc.rs +++ b/benches/src/perfenc.rs @@ -68,7 +68,7 @@ async fn main() -> Result<(), anyhow::Error> { let mut updates = DisplayUpdates::new(file, DesktopSize { width, height }, fps); while let Some(up) = updates.next_update().await? { if let DisplayUpdate::Bitmap(ref up) = up { - total_raw += up.data.len() as u64; + total_raw += u64::try_from(up.data.len())?; } else { eprintln!("Invalid update"); break; @@ -78,7 +78,7 @@ async fn main() -> Result<(), anyhow::Error> { let Some(frag) = iter.next().await else { break; }; - let len = frag?.data.len() as u64; + let len = u64::try_from(frag?.data.len())?; total_enc += len; } n_updates += 1; @@ -87,6 +87,7 @@ async fn main() -> Result<(), anyhow::Error> { } println!(); + #[expect(clippy::as_conversions, reason = "casting u64 to f64")] let ratio = total_enc as f64 / total_raw as f64; let percent = 100.0 - ratio * 100.0; println!("Encoder: {encoder:?}"); diff --git a/clippy.toml b/clippy.toml index dd6a9fe4..17e7e90f 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,4 +1,4 @@ -msrv = "1.84" +msrv = "1.87" semicolon-outside-block-ignore-multiline = true accept-comment-above-statement = true accept-comment-above-attributes = true diff --git a/crates/ironrdp-client/src/app.rs b/crates/ironrdp-client/src/app.rs index 9f579c17..bcce8d4f 100644 --- a/crates/ironrdp-client/src/app.rs +++ b/crates/ironrdp-client/src/app.rs @@ -66,6 +66,7 @@ impl App { let Some((window, _)) = self.window.as_mut() else { return; }; + #[expect(clippy::as_conversions, reason = "casting f64 to u32")] let scale_factor = (window.scale_factor() * 100.0) as u32; let width = u16::try_from(size.width).expect("reasonable width"); @@ -222,8 +223,10 @@ impl ApplicationHandler for App { } WindowEvent::CursorMoved { position, .. } => { let win_size = window.inner_size(); - let x = (position.x / win_size.width as f64 * self.buffer_size.0 as f64) as u16; - let y = (position.y / win_size.height as f64 * self.buffer_size.1 as f64) as u16; + #[expect(clippy::as_conversions, reason = "casting f64 to u16")] + let x = (position.x / f64::from(win_size.width) * f64::from(self.buffer_size.0)) as u16; + #[expect(clippy::as_conversions, reason = "casting f64 to u16")] + let y = (position.y / f64::from(win_size.height) * f64::from(self.buffer_size.1)) as u16; let operation = ironrdp::input::Operation::MouseMove(ironrdp::input::MousePosition { x, y }); let input_events = self.input_database.apply(core::iter::once(operation)); @@ -239,6 +242,7 @@ impl ApplicationHandler for App { operations.push(ironrdp::input::Operation::WheelRotations( ironrdp::input::WheelRotations { is_vertical: false, + #[expect(clippy::as_conversions, reason = "casting f32 to i16")] rotation_units: (delta_x * 100.) as i16, }, )); @@ -248,6 +252,7 @@ impl ApplicationHandler for App { operations.push(ironrdp::input::Operation::WheelRotations( ironrdp::input::WheelRotations { is_vertical: true, + #[expect(clippy::as_conversions, reason = "casting f32 to i16")] rotation_units: (delta_y * 100.) as i16, }, )); @@ -258,6 +263,7 @@ impl ApplicationHandler for App { operations.push(ironrdp::input::Operation::WheelRotations( ironrdp::input::WheelRotations { is_vertical: false, + #[expect(clippy::as_conversions, reason = "casting f64 to i16")] rotation_units: delta.x as i16, }, )); @@ -267,6 +273,7 @@ impl ApplicationHandler for App { operations.push(ironrdp::input::Operation::WheelRotations( ironrdp::input::WheelRotations { is_vertical: true, + #[expect(clippy::as_conversions, reason = "casting f64 to i16")] rotation_units: delta.y as i16, }, )); diff --git a/crates/ironrdp-client/src/rdp.rs b/crates/ironrdp-client/src/rdp.rs index 53ecb034..c1bd9aef 100644 --- a/crates/ironrdp-client/src/rdp.rs +++ b/crates/ironrdp-client/src/rdp.rs @@ -235,7 +235,7 @@ async fn connect( let upgraded = ironrdp_tokio::mark_as_upgraded(should_upgrade, &mut connector); - let erased_stream = Box::new(upgraded_stream) as Box; + let erased_stream: Box = Box::new(upgraded_stream); let mut upgraded_framed = ironrdp_tokio::TokioFramed::new_with_leftover(erased_stream, leftover_bytes); let connection_result = ironrdp_tokio::connect_finalize( @@ -336,7 +336,7 @@ async fn connect_ws( .await?; let (ws, leftover_bytes) = framed.into_inner(); - let erased_stream = Box::new(ws) as Box; + let erased_stream: Box = Box::new(ws); let upgraded_framed = ironrdp_tokio::TokioFramed::new_with_leftover(erased_stream, leftover_bytes); Ok((connection_result, upgraded_framed)) diff --git a/crates/ironrdp-cliprdr-native/src/windows/clipboard_data_ref.rs b/crates/ironrdp-cliprdr-native/src/windows/clipboard_data_ref.rs index b95d7b3c..06672142 100644 --- a/crates/ironrdp-cliprdr-native/src/windows/clipboard_data_ref.rs +++ b/crates/ironrdp-cliprdr-native/src/windows/clipboard_data_ref.rs @@ -27,7 +27,7 @@ impl<'a> ClipboardDataRef<'a> { }; // SAFETY: It is safe to call `GlobalLock` on the valid handle. - let data = unsafe { GlobalLock(handle) } as *const u8; + let data = unsafe { GlobalLock(handle) }.cast::().cast_const(); if data.is_null() { // Can't lock data handle, handle is not valid anymore (e.g. clipboard has changed) diff --git a/crates/ironrdp-cliprdr-native/src/windows/clipboard_impl.rs b/crates/ironrdp-cliprdr-native/src/windows/clipboard_impl.rs index ba1dba7e..66632846 100644 --- a/crates/ironrdp-cliprdr-native/src/windows/clipboard_impl.rs +++ b/crates/ironrdp-cliprdr-native/src/windows/clipboard_impl.rs @@ -1,3 +1,4 @@ +use core::ptr::with_exposed_provenance_mut; use core::time::Duration; use std::collections::HashSet; use std::sync::mpsc; @@ -320,17 +321,19 @@ pub(crate) unsafe extern "system" fn clipboard_subproc( // SAFETY: `data` is a valid pointer, returned by `Box::into_raw`, transferred to OS earlier // via `SetWindowSubclass` call. - let _ = unsafe { Box::from_raw(data as *mut WinClipboardImpl) }; + let _ = unsafe { Box::from_raw(with_exposed_provenance_mut::(data)) }; return LRESULT(0); } // SAFETY: `data` is a valid pointer, returned by `Box::into_raw`, transferred to OS earlier // via `SetWindowSubclass` call. - let ctx = unsafe { &mut *(data as *mut WinClipboardImpl) }; + let ctx = unsafe { &mut *(with_exposed_provenance_mut::(data)) }; match msg { // We need to keep track of window state to distinguish between local and remote copy - WM_ACTIVATE | WM_ACTIVATEAPP => ctx.window_is_active = wparam.0 != WA_INACTIVE as usize, // `as` conversion is fine for constants + WM_ACTIVATE | WM_ACTIVATEAPP => { + ctx.window_is_active = wparam.0 != usize::try_from(WA_INACTIVE).expect("WA_INACTIVE fits into usize") + } // Sent by the OS when OS clipboard content is changed WM_CLIPBOARDUPDATE => { // SAFETY: `GetClipboardOwner` is always safe to call. @@ -347,8 +350,9 @@ pub(crate) unsafe extern "system" fn clipboard_subproc( } // Sent by the OS when delay-rendered data is requested for rendering. WM_RENDERFORMAT => { - #[expect(clippy::cast_possible_truncation)] // should never truncate in practice - ctx.handle_event(BackendEvent::RenderFormat(ClipboardFormatId::new(wparam.0 as u32))); + ctx.handle_event(BackendEvent::RenderFormat(ClipboardFormatId::new( + u32::try_from(wparam.0).expect("should never truncate in practice"), + ))); } // Sent by the OS when all delay-rendered data is requested for rendering. WM_RENDERALLFORMATS => { diff --git a/crates/ironrdp-cliprdr-native/src/windows/mod.rs b/crates/ironrdp-cliprdr-native/src/windows/mod.rs index b488cfb5..7051fcfb 100644 --- a/crates/ironrdp-cliprdr-native/src/windows/mod.rs +++ b/crates/ironrdp-cliprdr-native/src/windows/mod.rs @@ -200,8 +200,14 @@ impl WinClipboard { // // SAFETY: `window` is a valid window handle, `clipboard_subproc` is in the static memory, // `ctx` is valid and its ownership is transferred to the subclass via `into_raw`. - let winapi_result = - unsafe { SetWindowSubclass(window, Some(clipboard_subproc), 0, Box::into_raw(ctx) as usize) }; + let winapi_result = unsafe { + SetWindowSubclass( + window, + Some(clipboard_subproc), + 0, + Box::into_raw(ctx).expose_provenance(), + ) + }; if winapi_result == FALSE { return Err(WinCliprdrError::WindowSubclass); diff --git a/crates/ironrdp-cliprdr-native/src/windows/utils.rs b/crates/ironrdp-cliprdr-native/src/windows/utils.rs index bd1120c8..198ca81d 100644 --- a/crates/ironrdp-cliprdr-native/src/windows/utils.rs +++ b/crates/ironrdp-cliprdr-native/src/windows/utils.rs @@ -26,7 +26,7 @@ impl GlobalMemoryBuffer { // - `dst` is valid for writes of `data.len()` bytes, we allocated enough above. // - Both `data` and `dst` are properly aligned: u8 alignment is 1 // - Memory regions are not overlapping, `dst` was allocated by us just above. - unsafe { core::ptr::copy_nonoverlapping(data.as_ptr(), dst as *mut u8, data.len()) }; + unsafe { core::ptr::copy_nonoverlapping(data.as_ptr(), dst.cast::(), data.len()) }; // SAFETY: We called `GlobalLock` on this handle just above. if let Err(error) = unsafe { GlobalUnlock(handle) } { diff --git a/crates/ironrdp-cliprdr/src/lib.rs b/crates/ironrdp-cliprdr/src/lib.rs index fa83f1ab..889ca92b 100644 --- a/crates/ironrdp-cliprdr/src/lib.rs +++ b/crates/ironrdp-cliprdr/src/lib.rs @@ -1,10 +1,6 @@ #![cfg_attr(doc, doc = include_str!("../README.md"))] #![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")] #![allow(clippy::arithmetic_side_effects)] // FIXME: remove -#![allow(clippy::cast_lossless)] // FIXME: remove -#![allow(clippy::cast_possible_truncation)] // FIXME: remove -#![allow(clippy::cast_possible_wrap)] // FIXME: remove -#![allow(clippy::cast_sign_loss)] // FIXME: remove pub mod backend; pub mod pdu; diff --git a/crates/ironrdp-connector/src/connection.rs b/crates/ironrdp-connector/src/connection.rs index 639cf93a..3018b1f7 100644 --- a/crates/ironrdp-connector/src/connection.rs +++ b/crates/ironrdp-connector/src/connection.rs @@ -699,9 +699,9 @@ fn create_gcc_blocks<'a>( desktop_physical_width: Some(0), // 0 per FreeRDP desktop_physical_height: Some(0), // 0 per FreeRDP desktop_orientation: if config.desktop_size.width > config.desktop_size.height { - Some(MonitorOrientation::Landscape as u16) + Some(MonitorOrientation::Landscape.as_u16()) } else { - Some(MonitorOrientation::Portrait as u16) + Some(MonitorOrientation::Portrait.as_u16()) }, desktop_scale_factor: Some(config.desktop_scale_factor), device_scale_factor: if config.desktop_scale_factor >= 100 && config.desktop_scale_factor <= 500 { diff --git a/crates/ironrdp-dvc/src/pdu.rs b/crates/ironrdp-dvc/src/pdu.rs index ef81987f..3f0c461e 100644 --- a/crates/ironrdp-dvc/src/pdu.rs +++ b/crates/ironrdp-dvc/src/pdu.rs @@ -200,7 +200,7 @@ impl Header { fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { ensure_fixed_part_size!(in: dst); - dst.write_u8(((self.cmd as u8) << 4) | (Into::::into(self.sp) << 2) | Into::::into(self.cb_id)); + dst.write_u8(((self.cmd.as_u8()) << 4) | (Into::::into(self.sp) << 2) | Into::::into(self.cb_id)); Ok(()) } @@ -235,6 +235,16 @@ enum Cmd { SoftSyncResponse = 0x09, } +impl Cmd { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] + fn as_u8(self) -> u8 { + self as u8 + } +} + impl TryFrom for Cmd { type Error = DecodeError; @@ -702,7 +712,7 @@ impl CapsVersion { fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { ensure_size!(in: dst, size: Self::size()); - dst.write_u16(*self as u16); + dst.write_u16(u16::from(*self)); Ok(()) } @@ -725,6 +735,10 @@ impl TryFrom for CapsVersion { } impl From for u16 { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] fn from(version: CapsVersion) -> Self { version as u16 } diff --git a/crates/ironrdp-fuzzing/src/oracles/mod.rs b/crates/ironrdp-fuzzing/src/oracles/mod.rs index b04a6788..2858316c 100644 --- a/crates/ironrdp-fuzzing/src/oracles/mod.rs +++ b/crates/ironrdp-fuzzing/src/oracles/mod.rs @@ -114,8 +114,8 @@ pub fn rdp6_decode_bitmap_stream_to_rgb24(input: &BitmapInput<'_>) { let _ = BitmapStreamDecoder::default().decode_bitmap_stream_to_rgb24( input.src, &mut out, - input.width as usize, - input.height as usize, + usize::from(input.width), + usize::from(input.height), ); } diff --git a/crates/ironrdp-graphics/src/color_conversion.rs b/crates/ironrdp-graphics/src/color_conversion.rs index b2554d37..2ec929b5 100644 --- a/crates/ironrdp-graphics/src/color_conversion.rs +++ b/crates/ironrdp-graphics/src/color_conversion.rs @@ -83,10 +83,15 @@ pub fn to_64x64_ycbcr_tile( /// Convert a 16-bit RDP color to RGB representation. Input value should be represented in /// little-endian format. pub fn rdp_16bit_to_rgb(color: u16) -> [u8; 3] { - let r = (((((color >> 11) & 0x1f) * 527) + 23) >> 6) as u8; - let g = (((((color >> 5) & 0x3f) * 259) + 33) >> 6) as u8; - let b = ((((color & 0x1f) * 527) + 23) >> 6) as u8; - [r, g, b] + #[expect(clippy::missing_panics_doc, reason = "unreachable panic (checked integer underflow)")] + let out = { + let r = u8::try_from(((((color >> 11) & 0x1f) * 527) + 23) >> 6).expect("max possible value is 255"); + let g = u8::try_from(((((color >> 5) & 0x3f) * 259) + 33) >> 6).expect("max possible value is 255"); + let b = u8::try_from((((color & 0x1f) * 527) + 23) >> 6).expect("max possible value is 255"); + [r, g, b] + }; + + out } #[derive(Debug)] diff --git a/crates/ironrdp-graphics/src/dwt.rs b/crates/ironrdp-graphics/src/dwt.rs index 6381cb54..5e13b8df 100644 --- a/crates/ironrdp-graphics/src/dwt.rs +++ b/crates/ironrdp-graphics/src/dwt.rs @@ -22,17 +22,21 @@ fn dwt_vertical(buffer: &[i16], dwt: &mut [i16]) { let h_index = l_index + SUBBAND_WIDTH * total_width; let src_index = y * total_width + x; - dwt[h_index] = ((i32::from(buffer[src_index + total_width]) - - ((i32::from(buffer[src_index]) - + i32::from(buffer[src_index + if n < SUBBAND_WIDTH - 1 { 2 * total_width } else { 0 }])) - >> 1)) - >> 1) as i16; - dwt[l_index] = (i32::from(buffer[src_index]) - + if n == 0 { - i32::from(dwt[h_index]) - } else { - (i32::from(dwt[h_index - total_width]) + i32::from(dwt[h_index])) >> 1 - }) as i16; + dwt[h_index] = i32_to_i16_possible_truncation( + (i32::from(buffer[src_index + total_width]) + - ((i32::from(buffer[src_index]) + + i32::from(buffer[src_index + if n < SUBBAND_WIDTH - 1 { 2 * total_width } else { 0 }])) + >> 1)) + >> 1, + ); + dwt[l_index] = i32_to_i16_possible_truncation( + i32::from(buffer[src_index]) + + if n == 0 { + i32::from(dwt[h_index]) + } else { + (i32::from(dwt[h_index - total_width]) + i32::from(dwt[h_index])) >> 1 + }, + ); } } } @@ -57,16 +61,20 @@ fn dwt_horizontal(mut buffer: &mut [i16], dwt: &[i16 let x = n * 2; // HL - hl[n] = ((i32::from(l_src[x + 1]) - - ((i32::from(l_src[x]) + i32::from(l_src[if n < SUBBAND_WIDTH - 1 { x + 2 } else { x }])) >> 1)) - >> 1) as i16; + hl[n] = i32_to_i16_possible_truncation( + (i32::from(l_src[x + 1]) + - ((i32::from(l_src[x]) + i32::from(l_src[if n < SUBBAND_WIDTH - 1 { x + 2 } else { x }])) >> 1)) + >> 1, + ); // LL - ll[n] = (i32::from(l_src[x]) - + if n == 0 { - i32::from(hl[n]) - } else { - (i32::from(hl[n - 1]) + i32::from(hl[n])) >> 1 - }) as i16; + ll[n] = i32_to_i16_possible_truncation( + i32::from(l_src[x]) + + if n == 0 { + i32::from(hl[n]) + } else { + (i32::from(hl[n - 1]) + i32::from(hl[n])) >> 1 + }, + ); } // H @@ -74,16 +82,20 @@ fn dwt_horizontal(mut buffer: &mut [i16], dwt: &[i16 let x = n * 2; // HH - hh[n] = ((i32::from(h_src[x + 1]) - - ((i32::from(h_src[x]) + i32::from(h_src[if n < SUBBAND_WIDTH - 1 { x + 2 } else { x }])) >> 1)) - >> 1) as i16; + hh[n] = i32_to_i16_possible_truncation( + (i32::from(h_src[x + 1]) + - ((i32::from(h_src[x]) + i32::from(h_src[if n < SUBBAND_WIDTH - 1 { x + 2 } else { x }])) >> 1)) + >> 1, + ); // LH - lh[n] = (i32::from(h_src[x]) - + if n == 0 { - i32::from(hh[n]) - } else { - (i32::from(hh[n - 1]) + i32::from(hh[n])) >> 1 - }) as i16; + lh[n] = i32_to_i16_possible_truncation( + i32::from(h_src[x]) + + if n == 0 { + i32::from(hh[n]) + } else { + (i32::from(hh[n - 1]) + i32::from(hh[n])) >> 1 + }, + ); } hl = &mut hl[SUBBAND_WIDTH..]; @@ -124,24 +136,30 @@ fn inverse_horizontal(mut buffer: &[i16], temp_buffer: &mut [i16], subband_width for _ in 0..subband_width { // Even coefficients - l_dst[0] = (i32::from(ll[0]) - ((i32::from(hl[0]) + i32::from(hl[0]) + 1) >> 1)) as i16; - h_dst[0] = (i32::from(lh[0]) - ((i32::from(hh[0]) + i32::from(hh[0]) + 1) >> 1)) as i16; + l_dst[0] = i32_to_i16_possible_truncation(i32::from(ll[0]) - ((i32::from(hl[0]) + i32::from(hl[0]) + 1) >> 1)); + h_dst[0] = i32_to_i16_possible_truncation(i32::from(lh[0]) - ((i32::from(hh[0]) + i32::from(hh[0]) + 1) >> 1)); for n in 1..subband_width { let x = n * 2; - l_dst[x] = (i32::from(ll[n]) - ((i32::from(hl[n - 1]) + i32::from(hl[n]) + 1) >> 1)) as i16; - h_dst[x] = (i32::from(lh[n]) - ((i32::from(hh[n - 1]) + i32::from(hh[n]) + 1) >> 1)) as i16; + l_dst[x] = + i32_to_i16_possible_truncation(i32::from(ll[n]) - ((i32::from(hl[n - 1]) + i32::from(hl[n]) + 1) >> 1)); + h_dst[x] = + i32_to_i16_possible_truncation(i32::from(lh[n]) - ((i32::from(hh[n - 1]) + i32::from(hh[n]) + 1) >> 1)); } // Odd coefficients for n in 0..subband_width - 1 { let x = n * 2; - l_dst[x + 1] = (i32::from(hl[n] << 1) + ((i32::from(l_dst[x]) + i32::from(l_dst[x + 2])) >> 1)) as i16; - h_dst[x + 1] = (i32::from(hh[n] << 1) + ((i32::from(h_dst[x]) + i32::from(h_dst[x + 2])) >> 1)) as i16; + l_dst[x + 1] = i32_to_i16_possible_truncation( + i32::from(hl[n] << 1) + ((i32::from(l_dst[x]) + i32::from(l_dst[x + 2])) >> 1), + ); + h_dst[x + 1] = i32_to_i16_possible_truncation( + i32::from(hh[n] << 1) + ((i32::from(h_dst[x]) + i32::from(h_dst[x + 2])) >> 1), + ); } let n = subband_width - 1; let x = n * 2; - l_dst[x + 1] = (i32::from(hl[n] << 1) + i32::from(l_dst[x])) as i16; - h_dst[x + 1] = (i32::from(hh[n] << 1) + i32::from(h_dst[x])) as i16; + l_dst[x + 1] = i32_to_i16_possible_truncation(i32::from(hl[n] << 1) + i32::from(l_dst[x])); + h_dst[x + 1] = i32_to_i16_possible_truncation(i32::from(hh[n] << 1) + i32::from(h_dst[x])); hl = &hl[subband_width..]; lh = &lh[subband_width..]; @@ -157,8 +175,9 @@ fn inverse_vertical(mut buffer: &mut [i16], mut temp_buffer: &[i16], subband_wid let total_width = subband_width * 2; for _ in 0..total_width { - buffer[0] = - (i32::from(temp_buffer[0]) - ((i32::from(temp_buffer[subband_width * total_width]) * 2 + 1) >> 1)) as i16; + buffer[0] = i32_to_i16_possible_truncation( + i32::from(temp_buffer[0]) - ((i32::from(temp_buffer[subband_width * total_width]) * 2 + 1) >> 1), + ); let mut l = temp_buffer; let mut lh = &temp_buffer[(subband_width - 1) * total_width..]; @@ -171,18 +190,28 @@ fn inverse_vertical(mut buffer: &mut [i16], mut temp_buffer: &[i16], subband_wid h = &h[total_width..]; // Even coefficients - dst[2 * total_width] = (i32::from(l[0]) - ((i32::from(lh[0]) + i32::from(h[0]) + 1) >> 1)) as i16; + dst[2 * total_width] = + i32_to_i16_possible_truncation(i32::from(l[0]) - ((i32::from(lh[0]) + i32::from(h[0]) + 1) >> 1)); // Odd coefficients - dst[total_width] = - (i32::from(lh[0] << 1) + ((i32::from(dst[0]) + i32::from(dst[2 * total_width])) >> 1)) as i16; + dst[total_width] = i32_to_i16_possible_truncation( + i32::from(lh[0] << 1) + ((i32::from(dst[0]) + i32::from(dst[2 * total_width])) >> 1), + ); dst = &mut dst[2 * total_width..]; } - dst[total_width] = (i32::from(lh[total_width] << 1) + ((i32::from(dst[0]) + i32::from(dst[0])) >> 1)) as i16; + dst[total_width] = i32_to_i16_possible_truncation( + i32::from(lh[total_width] << 1) + ((i32::from(dst[0]) + i32::from(dst[0])) >> 1), + ); temp_buffer = &temp_buffer[1..]; buffer = &mut buffer[1..]; } } + +#[expect(clippy::as_conversions)] +#[expect(clippy::cast_possible_truncation)] +fn i32_to_i16_possible_truncation(value: i32) -> i16 { + value as i16 +} diff --git a/crates/ironrdp-graphics/src/lib.rs b/crates/ironrdp-graphics/src/lib.rs index 03def02e..02d4104a 100644 --- a/crates/ironrdp-graphics/src/lib.rs +++ b/crates/ironrdp-graphics/src/lib.rs @@ -1,10 +1,6 @@ #![cfg_attr(doc, doc = include_str!("../README.md"))] #![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")] #![allow(clippy::arithmetic_side_effects)] // FIXME: remove -#![allow(clippy::cast_lossless)] // FIXME: remove -#![allow(clippy::cast_possible_truncation)] // FIXME: remove -#![allow(clippy::cast_possible_wrap)] // FIXME: remove -#![allow(clippy::cast_sign_loss)] // FIXME: remove pub mod color_conversion; pub mod diff; diff --git a/crates/ironrdp-graphics/src/pointer.rs b/crates/ironrdp-graphics/src/pointer.rs index 48b17927..26dbdd40 100644 --- a/crates/ironrdp-graphics/src/pointer.rs +++ b/crates/ironrdp-graphics/src/pointer.rs @@ -260,9 +260,12 @@ impl DecodedPointer { } else if target.should_premultiply_alpha() { // Calculate premultiplied alpha via integer arithmetic let with_premultiplied_alpha = [ - ((color[0] as u16 * color[0] as u16) >> 8) as u8, - ((color[1] as u16 * color[1] as u16) >> 8) as u8, - ((color[2] as u16 * color[2] as u16) >> 8) as u8, + u8::try_from((u16::from(color[0]) * u16::from(color[0])) >> 8) + .expect("(u16 >> 8) fits into u8"), + u8::try_from((u16::from(color[1]) * u16::from(color[1])) >> 8) + .expect("(u16 >> 8) fits into u8"), + u8::try_from((u16::from(color[2]) * u16::from(color[2])) >> 8) + .expect("(u16 >> 8) fits into u8"), color[3], ]; bitmap_data.extend_from_slice(&with_premultiplied_alpha); diff --git a/crates/ironrdp-graphics/src/quantization.rs b/crates/ironrdp-graphics/src/quantization.rs index 38c0f65b..c4ab7d97 100644 --- a/crates/ironrdp-graphics/src/quantization.rs +++ b/crates/ironrdp-graphics/src/quantization.rs @@ -11,7 +11,7 @@ pub fn decode(buffer: &mut [i16], quant: &Quant) { let (first_level, buffer) = buffer.split_at_mut(FIRST_LEVEL_SUBBANDS_COUNT * FIRST_LEVEL_SIZE); let (second_level, third_level) = buffer.split_at_mut(SECOND_LEVEL_SUBBANDS_COUNT * SECOND_LEVEL_SIZE); - let decode_chunk = |a: (&mut [i16], u8)| decode_block(a.0, a.1 as i16 - 1); + let decode_chunk = |a: (&mut [i16], u8)| decode_block(a.0, i16::from(a.1) - 1); first_level .chunks_mut(FIRST_LEVEL_SIZE) @@ -49,7 +49,7 @@ pub fn encode(buffer: &mut [i16], quant: &Quant) { let (first_level, buffer) = buffer.split_at_mut(FIRST_LEVEL_SUBBANDS_COUNT * FIRST_LEVEL_SIZE); let (second_level, third_level) = buffer.split_at_mut(SECOND_LEVEL_SUBBANDS_COUNT * SECOND_LEVEL_SIZE); - let encode_chunk = |a: (&mut [i16], u8)| encode_block(a.0, a.1 as i16 - 1); + let encode_chunk = |a: (&mut [i16], u8)| encode_block(a.0, i16::from(a.1) - 1); first_level .chunks_mut(FIRST_LEVEL_SIZE) diff --git a/crates/ironrdp-graphics/src/rdp6/bitmap_stream/decoder.rs b/crates/ironrdp-graphics/src/rdp6/bitmap_stream/decoder.rs index 0e26231e..d5f07b67 100644 --- a/crates/ironrdp-graphics/src/rdp6/bitmap_stream/decoder.rs +++ b/crates/ironrdp-graphics/src/rdp6/bitmap_stream/decoder.rs @@ -195,8 +195,9 @@ impl<'a> BitmapStreamDecoderImpl<'a> { } fn write_aycocg_planes_to_rgb24(&self, params: AYCoCgParams, planes: &[u8], dst: &mut Vec) { - #![allow(clippy::similar_names)] // It’s hard to find better names for co, cg, etc. - let sample_shift = params.chroma_subsampling as usize; + #![allow(clippy::similar_names, reason = "it’s hard to find better names for co, cg, etc")] + + let sample_shift = usize::from(params.chroma_subsampling); let (y_offset, co_offset, cg_offset) = ( self.color_plane_offsets[0], @@ -265,12 +266,13 @@ fn ycocg_with_cll_to_rgb(cll: u8, y: u8, co: u8, cg: u8) -> Rgb { // |R| |1 1/2 -1/2| |Y | // |G| = |1 0 1/2| * |Co| // |B| |1 -1/2 -1/2| |Cg| - let chroma_shift = (cll - 1) as usize; + let chroma_shift = cll - 1; - let clip_i16 = |v: i16| v.clamp(0, 255) as u8; + let clip_i16 = + |v: i16| u8::try_from(v.clamp(0, 255)).expect("fits into u8 because the value is clamped to [0..256]"); - let co_signed = (co << chroma_shift) as i8; - let cg_signed = (cg << chroma_shift) as i8; + let co_signed = (co << chroma_shift).cast_signed(); + let cg_signed = (cg << chroma_shift).cast_signed(); let y = i16::from(y); let co = i16::from(co_signed); diff --git a/crates/ironrdp-graphics/src/rdp6/rle.rs b/crates/ironrdp-graphics/src/rdp6/rle.rs index 7f74cb57..7e9a66fc 100644 --- a/crates/ironrdp-graphics/src/rdp6/rle.rs +++ b/crates/ironrdp-graphics/src/rdp6/rle.rs @@ -93,9 +93,9 @@ impl RlePlaneDecoder { let raw_bytes_field = (control_byte >> 4) & 0x0F; let (run_length, raw_bytes_count) = match rle_bytes_field { - 1 => (16 + raw_bytes_field as usize, 0), - 2 => (32 + raw_bytes_field as usize, 0), - rle_control => (rle_control as usize, raw_bytes_field as usize), + 1 => (16 + usize::from(raw_bytes_field), 0), + 2 => (32 + usize::from(raw_bytes_field), 0), + rle_control => (usize::from(rle_control), usize::from(raw_bytes_field)), }; self.decoded_data_len = raw_bytes_count + run_length; @@ -207,7 +207,8 @@ impl RleEncoderScanlineIterator { } fn delta_value(prev: u8, next: u8) -> u8 { - let mut result = (next as i16 - prev as i16) as u8; + let mut result = u8::try_from((i16::from(next) - i16::from(prev)) & 0xFF) + .expect("masking with 0xFF ensures that the value fits into u8"); // bit magic from 3.1.9.2.1 of [MS-RDPEGDI]. if result < 128 { @@ -326,7 +327,10 @@ impl RlePlaneEncoder { raw = &raw[15..]; } - let control = ((raw.len() as u8) << 4) + cmp::min(run, 15) as u8; + let raw_len = u8::try_from(raw.len()).expect("max value is guaranteed to be 15 due to the prior while loop"); + let run_capped = u8::try_from(cmp::min(run, 15)).expect("max value is guaranteed to be 15"); + + let control = (raw_len << 4) + run_capped; ensure_size!(dst: dst, size: raw.len() + 1); @@ -352,7 +356,8 @@ impl RlePlaneEncoder { while run >= 16 { ensure_size!(dst: dst, size: 1); - let current = cmp::min(run, MAX_DECODED_SEGMENT_SIZE) as u8; + let current = u8::try_from(cmp::min(run, MAX_DECODED_SEGMENT_SIZE)) + .expect("max value is guaranteed to be MAX_DECODED_SEGMENT_SIZE (47)"); let c_raw_bytes = cmp::min(current / 16, 2); let n_run_length = current - c_raw_bytes * 16; @@ -361,7 +366,7 @@ impl RlePlaneEncoder { dst.write_u8(control); written += 1; - run -= current as usize; + run -= usize::from(current); } if run > 0 { diff --git a/crates/ironrdp-graphics/src/rlgr.rs b/crates/ironrdp-graphics/src/rlgr.rs index 31dc09b2..3fc33ffa 100644 --- a/crates/ironrdp-graphics/src/rlgr.rs +++ b/crates/ironrdp-graphics/src/rlgr.rs @@ -63,17 +63,23 @@ impl<'a> BitStream<'a> { } pub fn encode(mode: EntropyAlgorithm, input: &[i16], tile: &mut [u8]) -> Result { - let mut k: u32 = 1; - let kr: u32 = 1; - let mut kp: u32 = k << LS_GR; - let mut krp: u32 = kr << LS_GR; + #![expect( + clippy::as_conversions, + reason = "u32-to-usize and usize-to-u32 conversions, mostly fine, and hot loop" + )] if input.is_empty() { return Err(RlgrError::EmptyTile); } + let mut k: u32 = 1; + let kr: u32 = 1; + let mut kp: u32 = k << LS_GR; + let mut krp: u32 = kr << LS_GR; let mut bits = BitStream::new(tile); + let mut input = input.iter().peekable(); + while input.peek().is_some() { match CompressionMode::from(k) { CompressionMode::RunLength => { @@ -98,7 +104,7 @@ pub fn encode(mode: EntropyAlgorithm, input: &[i16], tile: &mut [u8]) -> Result< bits.output_bits(k as usize, nz); if let Some(val) = input.next() { - let mag = val.unsigned_abs() as u32; + let mag = u32::from(val.unsigned_abs()); bits.output_bit(1, *val < 0); code_gr(&mut bits, &mut krp, mag - 1); } @@ -152,37 +158,53 @@ pub fn encode(mode: EntropyAlgorithm, input: &[i16], tile: &mut [u8]) -> Result< fn get_2magsign(val: i16) -> u32 { let sign = if val < 0 { 1 } else { 0 }; - (val.unsigned_abs() as u32) * 2 - sign + (u32::from(val.unsigned_abs())) * 2 - sign } fn code_gr(bits: &mut BitStream<'_>, krp: &mut u32, val: u32) { - let kr = (*krp >> LS_GR) as usize; - let vk = (val >> kr) as usize; + #![expect( + clippy::as_conversions, + reason = "u32-to-usize and usize-to-u32 conversions, mostly fine, and hot loop" + )] - bits.output_bit(vk, true); + let kr = (*krp >> LS_GR) as usize; + + let vk = val >> kr; + let vk_usize = vk as usize; + + bits.output_bit(vk_usize, true); bits.output_bit(1, false); + if kr != 0 { let remainder = val & ((1 << kr) - 1); bits.output_bits(kr, remainder); } + if vk == 0 { *krp = krp.saturating_sub(2); } else if vk > 1 { - *krp = min(*krp + vk as u32, KP_MAX); + *krp = min(*krp + vk, KP_MAX); } } pub fn decode(mode: EntropyAlgorithm, tile: &[u8], mut output: &mut [i16]) -> Result<(), RlgrError> { - let mut k: u32 = 1; - let mut kr: u32 = 1; - let mut kp: u32 = k << LS_GR; - let mut krp: u32 = kr << LS_GR; + #![expect( + clippy::as_conversions, + clippy::cast_possible_truncation, + reason = "u32-to-usize and usize-to-u32 conversions, mostly fine, and hot loop" + )] if tile.is_empty() { return Err(RlgrError::EmptyTile); } + let mut k: u32 = 1; + let mut kr: u32 = 1; + let mut kp: u32 = k << LS_GR; + let mut krp: u32 = kr << LS_GR; + let mut bits = Bits::new(BitSlice::from_slice(tile)); + while !bits.is_empty() && !output.is_empty() { match CompressionMode::from(k) { CompressionMode::RunLength => { @@ -201,7 +223,7 @@ pub fn decode(mode: EntropyAlgorithm, tile: &[u8], mut output: &mut [i16]) -> Re kp = kp.saturating_sub(DN_GR); k = kp >> LS_GR; - let magnitude = compute_rl_magnitude(sign_bit, code_remainder); + let magnitude = compute_rl_magnitude(sign_bit, code_remainder)?; let size = min(run as usize, output.len()); fill(&mut output[..size], 0); @@ -218,7 +240,7 @@ pub fn decode(mode: EntropyAlgorithm, tile: &[u8], mut output: &mut [i16]) -> Re match mode { EntropyAlgorithm::Rlgr1 => { - let magnitude = compute_rlgr1_magnitude(code_remainder, &mut k, &mut kp); + let magnitude = compute_rlgr1_magnitude(code_remainder, &mut k, &mut kp)?; write_byte!(output, magnitude); } EntropyAlgorithm::Rlgr3 => { @@ -234,10 +256,10 @@ pub fn decode(mode: EntropyAlgorithm, tile: &[u8], mut output: &mut [i16]) -> Re k = kp >> LS_GR; } - let magnitude = compute_rlgr3_magnitude(val1); + let magnitude = compute_rlgr3_magnitude(val1)?; write_byte!(output, magnitude); - let magnitude = compute_rlgr3_magnitude(val2); + let magnitude = compute_rlgr3_magnitude(val2)?; write_byte!(output, magnitude); } } @@ -245,7 +267,7 @@ pub fn decode(mode: EntropyAlgorithm, tile: &[u8], mut output: &mut [i16]) -> Re } } - // fill remaining buffer with zeros + // Fill remaining buffer with zeros. fill(output, 0); Ok(()) @@ -288,37 +310,41 @@ fn count_run(number_of_zeros: usize, k: &mut u32, kp: &mut u32) -> u32 { .sum() } -fn compute_rl_magnitude(sign_bit: u8, code_remainder: u32) -> i16 { +fn compute_rl_magnitude(sign_bit: u8, code_remainder: u32) -> Result { + let rl_magnitude = + i16::try_from(code_remainder + 1).map_err(|_| RlgrError::InvalidIntegralConversion("code remainder + 1"))?; + if sign_bit != 0 { - -((code_remainder + 1) as i16) + Ok(-rl_magnitude) } else { - (code_remainder + 1) as i16 + Ok(rl_magnitude) } } -fn compute_rlgr1_magnitude(code_remainder: u32, k: &mut u32, kp: &mut u32) -> i16 { +fn compute_rlgr1_magnitude(code_remainder: u32, k: &mut u32, kp: &mut u32) -> Result { if code_remainder == 0 { *kp = min(*kp + UQ_GR, KP_MAX); *k = *kp >> LS_GR; - 0 + Ok(0) } else { *kp = kp.saturating_sub(DQ_GR); *k = *kp >> LS_GR; if code_remainder % 2 != 0 { - -(((code_remainder + 1) >> 1) as i16) + Ok(-i16::try_from((code_remainder + 1) >> 1) + .map_err(|_| RlgrError::InvalidIntegralConversion("(code remainder + 1) >> 1"))?) } else { - (code_remainder >> 1) as i16 + i16::try_from(code_remainder >> 1).map_err(|_| RlgrError::InvalidIntegralConversion("code remainder >> 1")) } } } -fn compute_rlgr3_magnitude(val: u32) -> i16 { +fn compute_rlgr3_magnitude(val: u32) -> Result { if val % 2 != 0 { - -(((val + 1) >> 1) as i16) + Ok(-i16::try_from((val + 1) >> 1).map_err(|_| RlgrError::InvalidIntegralConversion("(val + 1) >> 1"))?) } else { - (val >> 1) as i16 + i16::try_from(val >> 1).map_err(|_| RlgrError::InvalidIntegralConversion("val >> 1")) } } @@ -335,11 +361,17 @@ fn compute_n_index(code_remainder: u32) -> usize { } fn update_parameters_according_to_number_of_ones(number_of_ones: usize, kr: &mut u32, krp: &mut u32) { + #![expect( + clippy::as_conversions, + clippy::cast_possible_truncation, + reason = "usize-to-u32 conversions, hot loop" + )] + if number_of_ones == 0 { *krp = (*krp).saturating_sub(2); *kr = *krp >> LS_GR; } else if number_of_ones > 1 { - *krp = min(*krp + number_of_ones as u32, KP_MAX); + *krp = min(*krp + (number_of_ones as u32), KP_MAX); *kr = *krp >> LS_GR; } } @@ -365,6 +397,7 @@ pub enum RlgrError { Io(io::Error), Yuv(YuvError), EmptyTile, + InvalidIntegralConversion(&'static str), } impl core::fmt::Display for RlgrError { @@ -373,6 +406,7 @@ impl core::fmt::Display for RlgrError { Self::Io(_) => write!(f, "IO error"), Self::Yuv(_) => write!(f, "YUV error"), Self::EmptyTile => write!(f, "the input tile is empty"), + Self::InvalidIntegralConversion(s) => write!(f, "invalid `{s}`: out of range integral type conversion"), } } } @@ -383,6 +417,7 @@ impl core::error::Error for RlgrError { Self::Io(error) => Some(error), Self::Yuv(error) => Some(error), Self::EmptyTile => None, + Self::InvalidIntegralConversion(_) => None, } } } diff --git a/crates/ironrdp-graphics/src/zgfx/control_messages.rs b/crates/ironrdp-graphics/src/zgfx/control_messages.rs index b6484419..9e696c03 100644 --- a/crates/ironrdp-graphics/src/zgfx/control_messages.rs +++ b/crates/ironrdp-graphics/src/zgfx/control_messages.rs @@ -23,12 +23,14 @@ impl<'a> SegmentedDataPdu<'a> { match descriptor { SegmentedDescriptor::Single => Ok(SegmentedDataPdu::Single(BulkEncodedData::from_buffer(buffer)?)), SegmentedDescriptor::Multipart => { - let segment_count = buffer.read_u16::()? as usize; - let uncompressed_size = buffer.read_u32::()? as usize; + let segment_count = usize::from(buffer.read_u16::()?); + let uncompressed_size = usize::try_from(buffer.read_u32::()?) + .map_err(|_| ZgfxError::InvalidIntegralConversion("segments uncompressed size"))?; let mut segments = Vec::with_capacity(segment_count); for _ in 0..segment_count { - let size = buffer.read_u32::()? as usize; + let size = usize::try_from(buffer.read_u32::()?) + .map_err(|_| ZgfxError::InvalidIntegralConversion("segment data size"))?; let (segment_data, new_buffer) = buffer.split_at(size); buffer = new_buffer; diff --git a/crates/ironrdp-graphics/src/zgfx/mod.rs b/crates/ironrdp-graphics/src/zgfx/mod.rs index daa3cb4e..688e655d 100644 --- a/crates/ironrdp-graphics/src/zgfx/mod.rs +++ b/crates/ironrdp-graphics/src/zgfx/mod.rs @@ -79,8 +79,8 @@ impl Decompressor { let mut bits = BitSlice::from_slice(encoded_data); // The value of the last byte indicates the number of unused bits in the final byte - bits = - &bits[..8 * (encoded_data.len() - 1) - *encoded_data.last().expect("encoded_data is not empty") as usize]; + bits = &bits + [..8 * (encoded_data.len() - 1) - usize::from(*encoded_data.last().expect("encoded_data is not empty"))]; let mut bits = Bits::new(bits); let mut bytes_written = 0; @@ -135,14 +135,15 @@ fn handle_match( distance_base: u32, history: &mut FixedCircularBuffer, output: &mut Vec, -) -> io::Result { +) -> Result { // Each token has been assigned a different base distance // and number of additional value bits to be added to compute the full distance. - let distance = (distance_base + bits.split_to(distance_value_size).load_be::()) as usize; + let distance = usize::try_from(distance_base + bits.split_to(distance_value_size).load_be::()) + .map_err(|_| ZgfxError::InvalidIntegralConversion("token's full distance"))?; if distance == 0 { - read_unencoded_bytes(bits, history, output) + read_unencoded_bytes(bits, history, output).map_err(ZgfxError::from) } else { read_encoded_bytes(bits, distance, history, output) } @@ -156,7 +157,7 @@ fn read_unencoded_bytes( // A match distance of zero is a special case, // which indicates that an unencoded run of bytes follows. // The count of bytes is encoded as a 15-bit value - let length = bits.split_to(15).load_be::() as usize; + let length = bits.split_to(15).load_be::(); if bits.remaining_bits_of_last_byte() > 0 { let pad_to_byte_boundary = 8 - bits.remaining_bits_of_last_byte(); @@ -179,7 +180,7 @@ fn read_encoded_bytes( distance: usize, history: &mut FixedCircularBuffer, output: &mut Vec, -) -> io::Result { +) -> Result { // A match length prefix follows the token and indicates // how many additional bits will be needed to get the full length // (the number of bytes to be copied). @@ -192,9 +193,12 @@ fn read_encoded_bytes( 3 } else { - let length = bits.split_to(length_token_size + 1).load_be::() as usize; + let length = bits.split_to(length_token_size + 1).load_be::(); - let base = 2u32.pow(length_token_size as u32 + 1) as usize; + let length_token_size = u32::try_from(length_token_size) + .map_err(|_| ZgfxError::InvalidIntegralConversion("length of the token size"))?; + + let base = 2usize.pow(length_token_size + 1); base + length }; @@ -441,6 +445,7 @@ pub enum ZgfxError { uncompressed_size: usize, }, TokenBitsNotFound, + InvalidIntegralConversion(&'static str), } impl core::fmt::Display for ZgfxError { @@ -457,6 +462,7 @@ impl core::fmt::Display for ZgfxError { "decompressed size of segments ({decompressed_size}) does not equal to uncompressed size ({uncompressed_size})", ), Self::TokenBitsNotFound => write!(f, "token bits not found"), + Self::InvalidIntegralConversion(type_name) => write!(f, "invalid `{type_name}`: out of range integral type conversion"), } } } @@ -469,6 +475,7 @@ impl core::error::Error for ZgfxError { Self::InvalidSegmentedDescriptor => None, Self::InvalidDecompressedSize { .. } => None, Self::TokenBitsNotFound => None, + Self::InvalidIntegralConversion(_) => None, } } } diff --git a/crates/ironrdp-input/src/lib.rs b/crates/ironrdp-input/src/lib.rs index 64a6a1b0..42292a38 100644 --- a/crates/ironrdp-input/src/lib.rs +++ b/crates/ironrdp-input/src/lib.rs @@ -24,6 +24,10 @@ pub enum MouseButton { } impl MouseButton { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] pub fn as_idx(self) -> usize { self as usize } @@ -78,7 +82,11 @@ impl Scancode { pub const fn from_u16(scancode: u16) -> Self { let extended = scancode & 0xE000 == 0xE000; - #[expect(clippy::cast_possible_truncation)] // truncating on purpose + #[expect( + clippy::as_conversions, + clippy::cast_possible_truncation, + reason = "truncating on purpose" + )] let code = scancode as u8; Self { code, extended } diff --git a/crates/ironrdp-mstsgu/src/lib.rs b/crates/ironrdp-mstsgu/src/lib.rs index d6d2e63b..b8b27c84 100644 --- a/crates/ironrdp-mstsgu/src/lib.rs +++ b/crates/ironrdp-mstsgu/src/lib.rs @@ -221,7 +221,8 @@ impl GwClient { let mut cur = ReadCursor::new(&msg); let hdr = PktHdr::decode(&mut cur).map_err(|e| custom_err!("Header Decode", e))?; - assert!(cur.len() >= hdr.length as usize - hdr.size()); + let header_length = usize::try_from(hdr.length).map_err(|_| Error::new("PktHdr too big", GwErrorKind::Decode))?; + assert!(cur.len() >= header_length - hdr.size()); match hdr.ty { PktTy::Keepalive => { continue; @@ -287,7 +288,10 @@ impl GwConn { let mut cur = ReadCursor::new(&msg); let hdr = PktHdr::decode(&mut cur).map_err(|_| Error::new("PktHdr", GwErrorKind::Decode))?; - if cur.len() != hdr.length as usize - hdr.size() { + + let header_length = + usize::try_from(hdr.length).map_err(|_| Error::new("PktHdr too big", GwErrorKind::Decode))?; + if cur.len() != header_length - hdr.size() { return Err(Error::new("read_packet", GwErrorKind::PacketEof)); } @@ -315,7 +319,7 @@ impl GwConn { async fn tunnel(&mut self) -> Result<(), Error> { let req = TunnelReqPkt { // Havent seen any server working without this. - caps: HttpCapsTy::MessagingConsentSign as u32, + caps: HttpCapsTy::MessagingConsentSign.as_u32(), fields_present: 0, ..TunnelReqPkt::default() }; diff --git a/crates/ironrdp-mstsgu/src/proto.rs b/crates/ironrdp-mstsgu/src/proto.rs index 0c7a0a9b..4efcf08d 100644 --- a/crates/ironrdp-mstsgu/src/proto.rs +++ b/crates/ironrdp-mstsgu/src/proto.rs @@ -37,6 +37,16 @@ pub(crate) enum PktTy { Keepalive = 0x0D, } +impl PktTy { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] + fn as_u16(self) -> u16 { + self as u16 + } +} + impl TryFrom for PktTy { type Error = (); @@ -78,7 +88,7 @@ impl Encode for PktHdr { fn encode(&self, dst: &mut WriteCursor<'_>) -> ironrdp_core::EncodeResult<()> { ensure_size!(in: dst, size: self.size()); - dst.write_u16(self.ty as u16); + dst.write_u16(self.ty.as_u16()); dst.write_u16(self._reserved); dst.write_u32(self.length); @@ -215,6 +225,7 @@ impl Encode for TunnelReqPkt { /// 2.2.5.3.9 HTTP_CAPABILITY_TYPE Enumeration #[repr(u32)] #[expect(dead_code)] +#[derive(Copy, Clone)] pub(crate) enum HttpCapsTy { QuarSOH = 1, IdleTimeout = 2, @@ -224,8 +235,19 @@ pub(crate) enum HttpCapsTy { UdpTransport = 0x20, } +impl HttpCapsTy { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] + pub(crate) fn as_u32(self) -> u32 { + self as u32 + } +} + /// 2.2.5.3.8 HTTP_TUNNEL_RESPONSE_FIELDS_PRESENT_FLAGS #[repr(u16)] +#[derive(Copy, Clone)] enum HttpTunnelResponseFields { TunnelID = 1, Caps = 2, @@ -234,6 +256,16 @@ enum HttpTunnelResponseFields { Consent = 0x10, } +impl HttpTunnelResponseFields { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] + fn as_u16(self) -> u16 { + self as u16 + } +} + /// 2.2.10.20 HTTP_TUNNEL_RESPONSE Structure #[derive(Debug, Default)] pub(crate) struct TunnelRespPkt { @@ -266,26 +298,26 @@ impl Decode<'_> for TunnelRespPkt { ..TunnelRespPkt::default() }; - if pkt.fields_present & (HttpTunnelResponseFields::TunnelID as u16) != 0 { + if pkt.fields_present & (HttpTunnelResponseFields::TunnelID.as_u16()) != 0 { ensure_size!(in: src, size: 4); pkt.tunnel_id = Some(src.read_u32()); } - if pkt.fields_present & (HttpTunnelResponseFields::Caps as u16) != 0 { + if pkt.fields_present & (HttpTunnelResponseFields::Caps.as_u16()) != 0 { ensure_size!(in: src, size: 4); pkt.caps_flags = Some(src.read_u32()); } - if pkt.fields_present & (HttpTunnelResponseFields::Soh as u16) != 0 { + if pkt.fields_present & (HttpTunnelResponseFields::Soh.as_u16()) != 0 { ensure_size!(in: src, size: 2 + 2); pkt.nonce = Some(src.read_u16()); - let len = src.read_u16(); - ensure_size!(in: src, size: len as usize); - pkt.server_cert = src.read_slice(len as usize).to_vec(); + let len = usize::from(src.read_u16()); + ensure_size!(in: src, size: len); + pkt.server_cert = src.read_slice(len).to_vec(); } - if pkt.fields_present & (HttpTunnelResponseFields::Consent as u16) != 0 { + if pkt.fields_present & (HttpTunnelResponseFields::Consent.as_u16()) != 0 { ensure_size!(in: src, size: 2); - let len = src.read_u16(); - ensure_size!(in: src, size: len as usize); - pkt.consent_msg = src.read_slice(len as usize).to_vec(); + let len = usize::from(src.read_u16()); + ensure_size!(in: src, size: len); + pkt.consent_msg = src.read_slice(len).to_vec(); } Ok(pkt) @@ -330,12 +362,12 @@ impl Decode<'_> for ExtendedAuthPkt { fn decode(src: &mut ReadCursor<'_>) -> ironrdp_core::DecodeResult { ensure_size!(in: src, size: 4 + 2); let error_code = src.read_u32(); - let len = src.read_u16(); - ensure_size!(in: src, size: len as usize); + let len = usize::from(src.read_u16()); + ensure_size!(in: src, size: len); Ok(ExtendedAuthPkt { error_code, - blob: src.read_slice(len as usize).to_vec(), + blob: src.read_slice(len).to_vec(), }) } } @@ -497,9 +529,9 @@ impl Decode<'_> for ChannelResp { } if resp.fields_present & 4 != 0 { ensure_size!(in: src, size: 2); - let len = src.read_u16(); - ensure_size!(in: src, size: len as usize); - resp.authn_cookie = src.read_slice(len as usize).to_vec(); + let len = usize::from(src.read_u16()); + ensure_size!(in: src, size: len); + resp.authn_cookie = src.read_slice(len).to_vec(); } Ok(resp) } @@ -538,10 +570,10 @@ impl Encode for DataPkt<'_> { impl<'a> Decode<'a> for DataPkt<'a> { fn decode(src: &mut ReadCursor<'a>) -> ironrdp_core::DecodeResult { ensure_size!(in: src, size: 2); - let len = src.read_u16(); - ensure_size!(in: src, size: len as usize); + let len = usize::from(src.read_u16()); + ensure_size!(in: src, size: len); Ok(DataPkt { - data: src.read_slice(len as usize), + data: src.read_slice(len), }) } } diff --git a/crates/ironrdp-pdu/src/gcc/monitor_extended_data.rs b/crates/ironrdp-pdu/src/gcc/monitor_extended_data.rs index f7fc7f9e..334c327c 100644 --- a/crates/ironrdp-pdu/src/gcc/monitor_extended_data.rs +++ b/crates/ironrdp-pdu/src/gcc/monitor_extended_data.rs @@ -95,7 +95,7 @@ impl Encode for ExtendedMonitorInfo { dst.write_u32(self.physical_width); dst.write_u32(self.physical_height); - dst.write_u32(self.orientation.as_u32()); + dst.write_u32(u32::from(self.orientation.as_u16())); dst.write_u32(self.desktop_scale_factor); dst.write_u32(self.device_scale_factor); @@ -132,7 +132,7 @@ impl<'de> Decode<'de> for ExtendedMonitorInfo { } } -#[repr(u32)] +#[repr(u16)] #[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive)] pub enum MonitorOrientation { Landscape = 0, @@ -146,7 +146,7 @@ impl MonitorOrientation { clippy::as_conversions, reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" )] - fn as_u32(self) -> u32 { - self as u32 + pub fn as_u16(self) -> u16 { + self as u16 } } diff --git a/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs/mod.rs b/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs/mod.rs index 66a9f8c0..f588bc42 100644 --- a/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs/mod.rs +++ b/crates/ironrdp-pdu/src/rdp/capability_sets/bitmap_codecs/mod.rs @@ -637,6 +637,10 @@ pub enum EntropyBits { } impl EntropyBits { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] fn as_u8(self) -> u8 { self as u8 } diff --git a/crates/ironrdp-pdu/src/rdp/server_error_info.rs b/crates/ironrdp-pdu/src/rdp/server_error_info.rs index 01b74bee..28a51521 100644 --- a/crates/ironrdp-pdu/src/rdp/server_error_info.rs +++ b/crates/ironrdp-pdu/src/rdp/server_error_info.rs @@ -412,6 +412,10 @@ impl RdpSpecificCode { } } + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] fn as_u32(self) -> u32 { self as u32 } diff --git a/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/server.rs b/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/server.rs index 3317895c..4684d0a9 100644 --- a/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/server.rs +++ b/crates/ironrdp-pdu/src/rdp/vc/dvc/gfx/graphics_messages/server.rs @@ -976,6 +976,10 @@ pub enum PixelFormat { } impl PixelFormat { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] fn as_u8(self) -> u8 { self as u8 } diff --git a/crates/ironrdp-rdpdr/src/lib.rs b/crates/ironrdp-rdpdr/src/lib.rs index 48062bf6..e5e1191f 100644 --- a/crates/ironrdp-rdpdr/src/lib.rs +++ b/crates/ironrdp-rdpdr/src/lib.rs @@ -1,10 +1,6 @@ #![cfg_attr(doc, doc = include_str!("../README.md"))] #![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")] #![allow(clippy::arithmetic_side_effects)] // FIXME: remove -#![allow(clippy::cast_lossless)] // FIXME: remove -#![allow(clippy::cast_possible_truncation)] // FIXME: remove -#![allow(clippy::cast_possible_wrap)] // FIXME: remove -#![allow(clippy::cast_sign_loss)] // FIXME: remove use ironrdp_core::{decode_cursor, impl_as_any, ReadCursor}; use ironrdp_pdu::gcc::ChannelName; diff --git a/crates/ironrdp-rdpdr/src/pdu/efs.rs b/crates/ironrdp-rdpdr/src/pdu/efs.rs index 37633d4e..5c669f29 100644 --- a/crates/ironrdp-rdpdr/src/pdu/efs.rs +++ b/crates/ironrdp-rdpdr/src/pdu/efs.rs @@ -154,9 +154,15 @@ impl ClientNameRequest { pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { ensure_size!(in: dst, size: self.size()); + + let encoded_computer_name_length = cast_length!( + "encoded computer name length", + encoded_str_len(self.computer_name(), self.unicode_flag().into(), true) + )?; + dst.write_u32(self.unicode_flag().into()); dst.write_u32(0); // // CodePage (4 bytes): it MUST be set to 0 - dst.write_u32(encoded_str_len(self.computer_name(), self.unicode_flag().into(), true) as u32); + dst.write_u32(encoded_computer_name_length); write_string_to_cursor(dst, self.computer_name(), self.unicode_flag().into(), true) } @@ -186,6 +192,10 @@ impl From for CharacterSet { } impl From for u32 { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] fn from(val: ClientNameRequestUnicodeFlag) -> Self { val as u32 } @@ -431,7 +441,7 @@ impl CapabilityHeader { fn new_general() -> Self { Self { cap_type: CapabilityType::General, - length: (Self::SIZE + GeneralCapabilitySet::SIZE) as u16, + length: u16::try_from(Self::SIZE + GeneralCapabilitySet::SIZE).expect("value fits into u16"), version: GENERAL_CAPABILITY_VERSION_02, } } @@ -439,7 +449,7 @@ impl CapabilityHeader { fn new_smartcard() -> Self { Self { cap_type: CapabilityType::Smartcard, - length: Self::SIZE as u16, + length: u16::try_from(Self::SIZE).expect("value fits into u16"), version: SMARTCARD_CAPABILITY_VERSION_01, } } @@ -447,7 +457,7 @@ impl CapabilityHeader { fn new_drive() -> Self { Self { cap_type: CapabilityType::Drive, - length: Self::SIZE as u16, + length: u16::try_from(Self::SIZE).expect("value fits into u16"), version: DRIVE_CAPABILITY_VERSION_02, } } @@ -490,6 +500,10 @@ enum CapabilityType { } impl From for u16 { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] fn from(cap_type: CapabilityType) -> Self { cap_type as u16 } @@ -990,6 +1004,10 @@ pub enum DeviceType { } impl From for u32 { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] fn from(device_type: DeviceType) -> Self { device_type as u32 } @@ -1211,6 +1229,10 @@ impl TryFrom for MajorFunction { } impl From for u32 { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] fn from(major_function: MajorFunction) -> Self { major_function as u32 } @@ -1253,12 +1275,6 @@ impl From for u32 { } } -impl From for u8 { - fn from(minor_function: MinorFunction) -> Self { - minor_function.0 as u8 - } -} - /// [2.2.1.4.5] Device Control Request (DR_CONTROL_REQ) /// /// [2.2.1.4.5]: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/30662c80-ec6e-4ed1-9004-2e6e367bb59f diff --git a/crates/ironrdp-rdpdr/src/pdu/esc/mod.rs b/crates/ironrdp-rdpdr/src/pdu/esc/mod.rs index b7deefde..1b495a7b 100644 --- a/crates/ironrdp-rdpdr/src/pdu/esc/mod.rs +++ b/crates/ironrdp-rdpdr/src/pdu/esc/mod.rs @@ -574,6 +574,10 @@ impl ReturnCode { } impl From for u32 { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] fn from(val: ReturnCode) -> Self { val as u32 } @@ -1244,7 +1248,7 @@ impl rpce::HeaderlessDecode for TransmitCall { #[derive(Debug, PartialEq, Clone)] pub struct SCardIORequest { pub protocol: CardProtocol, - pub extra_bytes_length: u32, + pub extra_bytes_length: usize, pub extra_bytes: Vec, } @@ -1255,7 +1259,7 @@ impl ndr::Decode for SCardIORequest { { ensure_size!(in: src, size: size_of::() * 2); let protocol = CardProtocol::from_bits_retain(src.read_u32()); - let extra_bytes_length = src.read_u32(); + let extra_bytes_length = cast_length!("SCardIORequest", "extra_bytes_length", src.read_u32())?; let _extra_bytes_ptr = ndr::decode_ptr(src, index)?; let extra_bytes = Vec::new(); Ok(Self { @@ -1267,9 +1271,8 @@ impl ndr::Decode for SCardIORequest { fn decode_value(&mut self, src: &mut ReadCursor<'_>, charset: Option) -> DecodeResult<()> { expect_no_charset(charset)?; - let extra_bytes_length: usize = cast_length!("TransmitCall", "extra_bytes_length", self.extra_bytes_length)?; - ensure_size!(in: src, size: extra_bytes_length); - self.extra_bytes = src.read_slice(extra_bytes_length).to_vec(); + ensure_size!(in: src, size: self.extra_bytes_length); + self.extra_bytes = src.read_slice(self.extra_bytes_length).to_vec(); Ok(()) } } @@ -1277,8 +1280,11 @@ impl ndr::Decode for SCardIORequest { impl ndr::Encode for SCardIORequest { fn encode_ptr(&self, index: &mut u32, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { ensure_size!(in: dst, size: self.size_ptr()); + + let extra_bytes_length = cast_length!("SCardIORequest", "extra_bytes_length", self.extra_bytes_length)?; + dst.write_u32(self.protocol.bits()); - ndr::encode_ptr(Some(self.extra_bytes_length), index, dst) + ndr::encode_ptr(Some(extra_bytes_length), index, dst) } fn encode_value(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> { @@ -1292,7 +1298,7 @@ impl ndr::Encode for SCardIORequest { } fn size_value(&self) -> usize { - self.extra_bytes_length as usize + self.extra_bytes_length } } @@ -1485,6 +1491,10 @@ pub enum CardState { } impl From for u32 { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] fn from(val: CardState) -> Self { val as u32 } diff --git a/crates/ironrdp-rdpdr/src/pdu/esc/rpce.rs b/crates/ironrdp-rdpdr/src/pdu/esc/rpce.rs index 31c97bae..75f6a90f 100644 --- a/crates/ironrdp-rdpdr/src/pdu/esc/rpce.rs +++ b/crates/ironrdp-rdpdr/src/pdu/esc/rpce.rs @@ -244,6 +244,10 @@ impl TryFrom for Endianness { } impl From for u8 { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] fn from(endianness: Endianness) -> Self { endianness as u8 } diff --git a/crates/ironrdp-rdpdr/src/pdu/mod.rs b/crates/ironrdp-rdpdr/src/pdu/mod.rs index fa96cba0..33848882 100644 --- a/crates/ironrdp-rdpdr/src/pdu/mod.rs +++ b/crates/ironrdp-rdpdr/src/pdu/mod.rs @@ -374,6 +374,10 @@ impl TryFrom for Component { } impl From for u16 { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] fn from(component: Component) -> Self { component as u16 } @@ -454,6 +458,10 @@ impl Display for PacketId { } impl From for u16 { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] fn from(packet_id: PacketId) -> Self { packet_id as u16 } diff --git a/crates/ironrdp-rdpsnd-native/src/cpal.rs b/crates/ironrdp-rdpsnd-native/src/cpal.rs index 00faa8db..f91d4719 100644 --- a/crates/ironrdp-rdpsnd-native/src/cpal.rs +++ b/crates/ironrdp-rdpsnd-native/src/cpal.rs @@ -160,6 +160,10 @@ impl DecodeStream { } }; + #[expect( + clippy::as_conversions, + reason = "opus::Channels has no conversions to usize implemented" + )] let mut pcm = vec![0u8; nb_samples * chan as usize * size_of::()]; if let Err(error) = dec.decode(&pkt, bytemuck::cast_slice_mut(pcm.as_mut_slice()), false) { error!(?error, "Failed to decode an Opus packet"); diff --git a/crates/ironrdp-rdpsnd/src/client.rs b/crates/ironrdp-rdpsnd/src/client.rs index b90e7dc9..0cc8c6ed 100644 --- a/crates/ironrdp-rdpsnd/src/client.rs +++ b/crates/ironrdp-rdpsnd/src/client.rs @@ -80,7 +80,7 @@ impl Rdpsnd { server_format .formats - .get(format_no as usize) + .get(usize::from(format_no)) .ok_or_else(|| pdu_other_err!("invalid format")) } @@ -196,7 +196,7 @@ impl SvcProcessor for Rdpsnd { match pdu { // TODO: handle WaveInfo for < v8 pdu::ServerAudioOutputPdu::Wave2(pdu) => { - let format_no = pdu.format_no as usize; + let format_no = usize::from(pdu.format_no); let ts = pdu.audio_timestamp; self.handler.wave(format_no, ts, pdu.data); return Ok(self.wave_confirm(pdu.timestamp, pdu.block_no)?.into()); diff --git a/crates/ironrdp-rdpsnd/src/pdu/mod.rs b/crates/ironrdp-rdpsnd/src/pdu/mod.rs index eb3b45e1..258b239a 100644 --- a/crates/ironrdp-rdpsnd/src/pdu/mod.rs +++ b/crates/ironrdp-rdpsnd/src/pdu/mod.rs @@ -51,6 +51,10 @@ impl TryFrom for Version { } impl From for u16 { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] fn from(version: Version) -> Self { version as u16 } @@ -442,9 +446,8 @@ impl<'de> Decode<'de> for ClientAudioFormatPdu { ensure_fixed_part_size!(in: src); let flags = AudioFormatFlags::from_bits_truncate(src.read_u32()); - let volume = src.read_u32(); - let volume_left = (volume & 0xFFFF) as u16; - let volume_right = (volume >> 16) as u16; + let volume_left = src.read_u16(); + let volume_right = src.read_u16(); let pitch = src.read_u32(); let dgram_port = src.read_u16_be(); let n_formats = usize::from(src.read_u16()); @@ -489,6 +492,10 @@ impl TryFrom for QualityMode { } impl From for u16 { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] fn from(mode: QualityMode) -> Self { mode as u16 } @@ -626,7 +633,7 @@ impl<'de> Decode<'de> for TrainingPdu { ensure_fixed_part_size!(in: src); let timestamp = src.read_u16(); - let len = src.read_u16() as usize; + let len = usize::from(src.read_u16()); let data = if len != 0 { if len < Self::FIXED_PART_SIZE + ServerAudioOutputPdu::FIXED_PART_SIZE { return Err(invalid_field_err!("TrainingPdu::wPackSize", "too small")); @@ -839,7 +846,7 @@ impl Encode for WavePdu<'_> { impl WavePdu<'_> { fn decode(src: &mut ReadCursor<'_>, body_size: u16) -> DecodeResult { let info = WaveInfoPdu::decode(src)?; - let body_size = body_size as usize; + let body_size = usize::from(body_size); let data_len = body_size .checked_sub(info.size()) .ok_or_else(|| invalid_field_err!("Length", "WaveInfo body_size is too small"))?; @@ -1090,9 +1097,8 @@ impl<'de> Decode<'de> for VolumePdu { fn decode(src: &mut ReadCursor<'de>) -> DecodeResult { ensure_fixed_part_size!(in: src); - let volume = src.read_u32(); - let volume_left = (volume & 0xFFFF) as u16; - let volume_right = (volume >> 16) as u16; + let volume_left = src.read_u16(); + let volume_right = src.read_u16(); Ok(Self { volume_left, diff --git a/crates/ironrdp-server/src/encoder/bitmap.rs b/crates/ironrdp-server/src/encoder/bitmap.rs index e7a78c7b..8810e433 100644 --- a/crates/ironrdp-server/src/encoder/bitmap.rs +++ b/crates/ironrdp-server/src/encoder/bitmap.rs @@ -19,7 +19,7 @@ pub(crate) struct BitmapEncoder { impl BitmapEncoder { pub(crate) fn new() -> Self { Self { - buffer: vec![0; u16::MAX as usize], + buffer: vec![0; usize::from(u16::MAX)], } } diff --git a/crates/ironrdp-server/src/encoder/mod.rs b/crates/ironrdp-server/src/encoder/mod.rs index 1e6950da..6355c232 100644 --- a/crates/ironrdp-server/src/encoder/mod.rs +++ b/crates/ironrdp-server/src/encoder/mod.rs @@ -31,6 +31,16 @@ enum CodecId { None = 0x0, } +impl CodecId { + #[expect( + clippy::as_conversions, + reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive" + )] + fn as_u8(self) -> u8 { + self as u8 + } +} + #[cfg_attr(feature = "__bench", visibility::make(pub))] #[derive(Debug)] pub(crate) struct UpdateEncoderCodecs { @@ -389,7 +399,7 @@ impl BitmapUpdateHandler for NoneHandler { for row in bitmap.data.chunks(bitmap.stride.get()).rev() { data.extend_from_slice(&row[..stride]); } - set_surface(bitmap, CodecId::None as u8, &data) + set_surface(bitmap, CodecId::None.as_u8(), &data) } } diff --git a/crates/ironrdp-server/src/handler.rs b/crates/ironrdp-server/src/handler.rs index a1497167..0bcf6519 100644 --- a/crates/ironrdp-server/src/handler.rs +++ b/crates/ironrdp-server/src/handler.rs @@ -97,9 +97,14 @@ impl From<(u16, fast_path::KeyboardFlags)> for KeyboardEvent { } impl From<(u16, scan_code::KeyboardFlags)> for KeyboardEvent { - #[expect(clippy::cast_possible_truncation)] // we are actually truncating the value + #[expect( + clippy::as_conversions, + clippy::cast_possible_truncation, + reason = "we are truncating the value on purpose" + )] fn from((key, flags): (u16, scan_code::KeyboardFlags)) -> Self { let extended = flags.contains(scan_code::KeyboardFlags::EXTENDED); + if flags.contains(scan_code::KeyboardFlags::RELEASE) { KeyboardEvent::Released { code: key as u8, @@ -131,7 +136,11 @@ impl From for KeyboardEvent { } impl From for KeyboardEvent { - #[expect(clippy::cast_possible_truncation)] // we are actually truncating the value + #[expect( + clippy::as_conversions, + clippy::cast_possible_truncation, + reason = "we are truncating the value on purpose" + )] fn from(value: SyncToggleFlags) -> Self { KeyboardEvent::Synchronize(SynchronizeFlags::from_bits_truncate(value.bits() as u8)) } diff --git a/crates/ironrdp-session/src/image.rs b/crates/ironrdp-session/src/image.rs index 7e27c3f1..88dedb54 100644 --- a/crates/ironrdp-session/src/image.rs +++ b/crates/ironrdp-session/src/image.rs @@ -72,7 +72,6 @@ struct PointerRenderingState { } #[expect(clippy::too_many_arguments)] -#[expect(clippy::cast_lossless)] // FIXME fn copy_cursor_data( from: &[u8], from_pos: (usize, usize), @@ -124,11 +123,17 @@ fn copy_cursor_data( continue; } - // Integer alpha blending, source represented as premultiplied alpha color, calculation in floating point - to[to_start + pixel * PIXEL_SIZE] = src_r + (((dest_r as u16) * (255 - src_a) as u16) >> 8) as u8; - to[to_start + pixel * PIXEL_SIZE + 1] = src_g + (((dest_g as u16) * (255 - src_a) as u16) >> 8) as u8; - to[to_start + pixel * PIXEL_SIZE + 2] = src_b + (((dest_b as u16) * (255 - src_a) as u16) >> 8) as u8; - // Framebuffer is always opaque, so we can skip alpha channel change + #[expect(clippy::as_conversions, reason = "(u16 >> 8) fits into u8 + hot loop")] + { + // Integer alpha blending, source represented as premultiplied alpha color, calculation in floating point + to[to_start + pixel * PIXEL_SIZE] = + src_r + ((u16::from(dest_r) * u16::from(255 - src_a)) >> 8) as u8; + to[to_start + pixel * PIXEL_SIZE + 1] = + src_g + ((u16::from(dest_g) * u16::from(255 - src_a)) >> 8) as u8; + to[to_start + pixel * PIXEL_SIZE + 2] = + src_b + ((u16::from(dest_b) * u16::from(255 - src_a)) >> 8) as u8; + // Framebuffer is always opaque, so we can skip alpha channel change + } } } else { to[to_start..to_start + width * PIXEL_SIZE] @@ -227,6 +232,13 @@ impl DecodedImage { return Ok(None); } + let pointer_src_rect_width = usize::from(self.pointer_src_rect.width()); + let pointer_src_rect_height = usize::from(self.pointer_src_rect.height()); + let pointer_draw_x = usize::from(self.pointer_draw_x); + let pointer_draw_y = usize::from(self.pointer_draw_y); + let width = usize::from(self.width); + let height = usize::from(self.height); + match &layer { PointerLayer::Background => { if self.pointer_backbuffer.is_empty() { @@ -237,15 +249,12 @@ impl DecodedImage { copy_cursor_data( &self.pointer_backbuffer, (0, 0), - self.pointer_src_rect.width() as usize * 4, + pointer_src_rect_width * 4, &mut self.data, - self.width as usize * 4, - (self.pointer_draw_x as usize, self.pointer_draw_y as usize), - ( - self.pointer_src_rect.width() as usize, - self.pointer_src_rect.height() as usize, - ), - (self.width as usize, self.height as usize), + width * 4, + (pointer_draw_x, pointer_draw_y), + (pointer_src_rect_width, pointer_src_rect_height), + (width, height), false, ); } @@ -254,37 +263,34 @@ impl DecodedImage { let buffer_size = self .pointer_backbuffer .len() - .max(self.pointer_src_rect.width() as usize * self.pointer_src_rect.height() as usize * 4); + .max(pointer_src_rect_width * pointer_src_rect_height * 4); self.pointer_backbuffer.resize(buffer_size, 0); copy_cursor_data( &self.data, - (self.pointer_draw_x as usize, self.pointer_draw_y as usize), - self.width as usize * 4, + (pointer_draw_x, pointer_draw_y), + width * 4, &mut self.pointer_backbuffer, - self.pointer_src_rect.width() as usize * 4, + pointer_src_rect_width * 4, (0, 0), - ( - self.pointer_src_rect.width() as usize, - self.pointer_src_rect.height() as usize, - ), - (self.width as usize, self.height as usize), + (pointer_src_rect_width, pointer_src_rect_height), + (width, height), false, ); // Draw pointer (with compositing) copy_cursor_data( pointer.bitmap_data.as_slice(), - (self.pointer_src_rect.left as usize, self.pointer_src_rect.top as usize), + ( + usize::from(self.pointer_src_rect.left), + usize::from(self.pointer_src_rect.top), + ), usize::from(pointer.width) * 4, &mut self.data, - self.width as usize * 4, - (self.pointer_draw_x as usize, self.pointer_draw_y as usize), - ( - self.pointer_src_rect.width() as usize, - self.pointer_src_rect.height() as usize, - ), - (self.width as usize, self.height as usize), + width * 4, + (pointer_draw_x, pointer_draw_y), + (pointer_src_rect_width, pointer_src_rect_height), + (width, height), true, ); } @@ -312,7 +318,6 @@ impl DecodedImage { } } - #[expect(clippy::cast_possible_wrap)] // FIXME fn recalculate_pointer_geometry(&mut self) { let x = self.pointer_x; let y = self.pointer_y; @@ -322,10 +327,10 @@ impl DecodedImage { _ => return, }; - let left_virtual = x as i16 - pointer.hotspot_x as i16; - let top_virtual = y as i16 - pointer.hotspot_y as i16; - let right_virtual = left_virtual + pointer.width as i16 - 1; - let bottom_virtual = top_virtual + pointer.height as i16 - 1; + let left_virtual = i32::from(x) - i32::from(pointer.hotspot_x); + let top_virtual = i32::from(y) - i32::from(pointer.hotspot_y); + let right_virtual = left_virtual + i32::from(pointer.width) - 1; + let bottom_virtual = top_virtual + i32::from(pointer.height) - 1; let (left, draw_x) = if left_virtual < 0 { // Cut left side if required @@ -342,7 +347,7 @@ impl DecodedImage { }; // Cut right side if required - let right = if right_virtual >= (self.width - 1) as i16 { + let right = if right_virtual >= i32::from(self.width - 1) { if draw_x + 1 >= self.width { // Pointer is completely out of bounds horizontally self.pointer_visible_on_screen = false; @@ -355,7 +360,7 @@ impl DecodedImage { }; // Cut bottom side if required - let bottom = if bottom_virtual >= (self.height - 1) as i16 { + let bottom = if bottom_virtual >= i32::from(self.height - 1) { if (draw_y + 1) >= self.height { // Pointer is completely out of bounds vertically self.pointer_visible_on_screen = false; @@ -539,7 +544,7 @@ impl DecodedImage { const SRC_COLOR_DEPTH: usize = 2; const DST_COLOR_DEPTH: usize = 4; - let image_width = self.width as usize; + let image_width = usize::from(self.width); let rectangle_width = usize::from(update_rectangle.width()); let top = usize::from(update_rectangle.top); let left = usize::from(update_rectangle.left); @@ -586,7 +591,7 @@ impl DecodedImage { const SRC_COLOR_DEPTH: usize = 3; const DST_COLOR_DEPTH: usize = 4; - let image_width = self.width as usize; + let image_width = usize::from(self.width); let top = usize::from(update_rectangle.top); let left = usize::from(update_rectangle.left); @@ -636,7 +641,7 @@ impl DecodedImage { const SRC_COLOR_DEPTH: usize = 4; const DST_COLOR_DEPTH: usize = 4; - let image_width = self.width as usize; + let image_width = usize::from(self.width); let rectangle_width = usize::from(update_rectangle.width()); let top = usize::from(update_rectangle.top); let left = usize::from(update_rectangle.left); diff --git a/crates/ironrdp-session/src/rfx.rs b/crates/ironrdp-session/src/rfx.rs index 7bd21d6d..4f115a6b 100644 --- a/crates/ironrdp-session/src/rfx.rs +++ b/crates/ironrdp-session/src/rfx.rs @@ -187,10 +187,11 @@ struct DecodingTileContext { impl DecodingTileContext { fn new() -> Self { + let tile_size = usize::from(TILE_SIZE); Self { - tile_output: vec![0; TILE_SIZE as usize * TILE_SIZE as usize * 4], - ycbcr_buffer: vec![vec![0; TILE_SIZE as usize * TILE_SIZE as usize]; 3], - ycbcr_temp_buffer: vec![0; TILE_SIZE as usize * TILE_SIZE as usize], + tile_output: vec![0; tile_size * tile_size * 4], + ycbcr_buffer: vec![vec![0; tile_size * tile_size]; 3], + ycbcr_temp_buffer: vec![0; tile_size * tile_size], } } } diff --git a/crates/ironrdp-testsuite-core/src/gcc.rs b/crates/ironrdp-testsuite-core/src/gcc.rs index d2fd9d2e..1ebe270e 100644 --- a/crates/ironrdp-testsuite-core/src/gcc.rs +++ b/crates/ironrdp-testsuite-core/src/gcc.rs @@ -42,6 +42,7 @@ const fn make_gcc_block_buffer(data_type: u16, buffer: &[u8]) -> let array = copy_slice(&data_type.to_le_bytes(), [0; N], 0); + #[expect(clippy::as_conversions, reason = "must be const casts")] let length = (buffer.len() + USER_HEADER_LEN) as u16; let array = copy_slice(&length.to_le_bytes(), array, 2); @@ -161,6 +162,7 @@ pub static SERVER_GCC_WITH_OPTIONAL_FIELDS: LazyLock = LazyLock data }); +#[expect(clippy::as_conversions, reason = "must be const casts")] pub const CLIENT_GCC_CORE_BLOCK_BUFFER: [u8; gcc_block_size( CLIENT_OPTIONAL_CORE_DATA_TO_SERVER_SELECTED_PROTOCOL_BUFFER, )] = make_gcc_block_buffer( @@ -168,18 +170,22 @@ pub const CLIENT_GCC_CORE_BLOCK_BUFFER: [u8; gcc_block_size( &CLIENT_OPTIONAL_CORE_DATA_TO_SERVER_SELECTED_PROTOCOL_BUFFER, ); +#[expect(clippy::as_conversions, reason = "must be const casts")] pub const CLIENT_GCC_SECURITY_BLOCK_BUFFER: [u8; gcc_block_size(CLIENT_SECURITY_DATA_BUFFER)] = make_gcc_block_buffer(ClientGccType::SecurityData as u16, &CLIENT_SECURITY_DATA_BUFFER); +#[expect(clippy::as_conversions, reason = "must be const casts")] pub const CLIENT_GCC_NETWORK_BLOCK_BUFFER: [u8; gcc_block_size(CLIENT_NETWORK_DATA_WITH_CHANNELS_BUFFER)] = make_gcc_block_buffer( ClientGccType::NetworkData as u16, &CLIENT_NETWORK_DATA_WITH_CHANNELS_BUFFER, ); +#[expect(clippy::as_conversions, reason = "must be const casts")] pub const CLIENT_GCC_CLUSTER_BLOCK_BUFFER: [u8; gcc_block_size(CLUSTER_DATA_BUFFER)] = make_gcc_block_buffer(ClientGccType::ClusterData as u16, &CLUSTER_DATA_BUFFER); +#[expect(clippy::as_conversions, reason = "must be const casts")] pub const CLIENT_GCC_MONITOR_BLOCK_BUFFER: [u8; gcc_block_size( crate::monitor_data::MONITOR_DATA_WITH_MONITORS_BUFFER, )] = make_gcc_block_buffer( @@ -187,6 +193,7 @@ pub const CLIENT_GCC_MONITOR_BLOCK_BUFFER: [u8; gcc_block_size( &crate::monitor_data::MONITOR_DATA_WITH_MONITORS_BUFFER, ); +#[expect(clippy::as_conversions, reason = "must be const casts")] pub const CLIENT_GCC_MONITOR_EXTENDED_BLOCK_BUFFER: [u8; gcc_block_size( crate::monitor_extended_data::MONITOR_DATA_WITH_MONITORS_BUFFER, )] = make_gcc_block_buffer( @@ -194,24 +201,28 @@ pub const CLIENT_GCC_MONITOR_EXTENDED_BLOCK_BUFFER: [u8; gcc_block_size( &crate::monitor_extended_data::MONITOR_DATA_WITH_MONITORS_BUFFER, ); +#[expect(clippy::as_conversions, reason = "must be const casts")] pub const SERVER_GCC_CORE_BLOCK_BUFFER: [u8; gcc_block_size(SERVER_CORE_DATA_TO_REQUESTED_PROTOCOL_BUFFER)] = make_gcc_block_buffer( ServerGccType::CoreData as u16, &SERVER_CORE_DATA_TO_REQUESTED_PROTOCOL_BUFFER, ); +#[expect(clippy::as_conversions, reason = "must be const casts")] pub const SERVER_GCC_NETWORK_BLOCK_BUFFER: [u8; gcc_block_size(SERVER_NETWORK_DATA_WITH_CHANNELS_ID_BUFFER)] = make_gcc_block_buffer( ServerGccType::NetworkData as u16, &SERVER_NETWORK_DATA_WITH_CHANNELS_ID_BUFFER, ); +#[expect(clippy::as_conversions, reason = "must be const casts")] pub const SERVER_GCC_SECURITY_BLOCK_BUFFER: [u8; gcc_block_size(SERVER_SECURITY_DATA_WITH_OPTIONAL_FIELDS_BUFFER)] = make_gcc_block_buffer( ServerGccType::SecurityData as u16, &SERVER_SECURITY_DATA_WITH_OPTIONAL_FIELDS_BUFFER, ); +#[expect(clippy::as_conversions, reason = "must be const casts")] pub const SERVER_GCC_MESSAGE_CHANNEL_BLOCK_BUFFER: [u8; gcc_block_size( crate::message_channel_data::SERVER_GCC_MESSAGE_CHANNEL_BLOCK_BUFFER, )] = make_gcc_block_buffer( @@ -219,6 +230,7 @@ pub const SERVER_GCC_MESSAGE_CHANNEL_BLOCK_BUFFER: [u8; gcc_block_size( &crate::message_channel_data::SERVER_GCC_MESSAGE_CHANNEL_BLOCK_BUFFER, ); +#[expect(clippy::as_conversions, reason = "must be const casts")] pub const SERVER_GCC_MULTI_TRANSPORT_CHANNEL_BLOCK_BUFFER: [u8; gcc_block_size( crate::multi_transport_channel_data::SERVER_GCC_MULTI_TRANSPORT_CHANNEL_BLOCK_BUFFER, )] = make_gcc_block_buffer( diff --git a/crates/ironrdp-testsuite-core/src/rdp.rs b/crates/ironrdp-testsuite-core/src/rdp.rs index 51d788bd..ae4a91a7 100644 --- a/crates/ironrdp-testsuite-core/src/rdp.rs +++ b/crates/ironrdp-testsuite-core/src/rdp.rs @@ -180,7 +180,7 @@ pub static SERVER_LICENSE_PDU: LazyLock = LazyLock::new(|| { state_transition: LicensingStateTransition::NoTransition, error_info: Vec::new(), }; - pdu.license_header.preamble_message_size = pdu.size() as u16; + pdu.license_header.preamble_message_size = u16::try_from(pdu.size()).unwrap(); pdu.into() }); pub static SERVER_DEMAND_ACTIVE_PDU: LazyLock = LazyLock::new(|| ShareControlHeader { diff --git a/crates/ironrdp-testsuite-core/src/security_data.rs b/crates/ironrdp-testsuite-core/src/security_data.rs index 336c7364..87a65c8e 100644 --- a/crates/ironrdp-testsuite-core/src/security_data.rs +++ b/crates/ironrdp-testsuite-core/src/security_data.rs @@ -65,6 +65,7 @@ pub static SERVER_SECURITY_DATA_WITH_MISMATCH_OF_REQUIRED_AND_OPTIONAL_FIELDS: L server_cert: SERVER_CERT_BUFFER.to_vec(), }); +#[expect(clippy::as_conversions, reason = "must be const casts")] pub const SERVER_SECURITY_DATA_WITH_OPTIONAL_FIELDS_BUFFER: [u8; 232] = concat_arrays!( SERVER_SECURITY_DATA_WITH_OPTIONAL_FIELDS_PREFIX_BUFFER, (SERVER_RANDOM_BUFFER.len() as u32).to_le_bytes(), @@ -73,6 +74,7 @@ pub const SERVER_SECURITY_DATA_WITH_OPTIONAL_FIELDS_BUFFER: [u8; 232] = concat_a SERVER_CERT_BUFFER ); +#[expect(clippy::as_conversions, reason = "must be const casts")] pub const SERVER_SECURITY_DATA_WITH_INVALID_SERVER_RANDOM_BUFFER: [u8; 233] = concat_arrays!( SERVER_SECURITY_DATA_WITH_OPTIONAL_FIELDS_PREFIX_BUFFER, (SERVER_RANDOM_BUFFER.len() as u32 + 1).to_le_bytes(), diff --git a/crates/ironrdp-testsuite-core/tests/dvc/data_first.rs b/crates/ironrdp-testsuite-core/tests/dvc/data_first.rs index 81c7605c..aee11e2b 100644 --- a/crates/ironrdp-testsuite-core/tests/dvc/data_first.rs +++ b/crates/ironrdp-testsuite-core/tests/dvc/data_first.rs @@ -9,6 +9,7 @@ const DATA: [u8; 12] = [0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x const EDGE_CASE_LENGTH: u32 = 0x639; const EDGE_CASE_CHANNEL_ID: u32 = 0x07; const EDGE_CASE_PREFIX: [u8; 4] = [0x24, 0x7, 0x39, 0x6]; +#[expect(clippy::as_conversions)] const EDGE_CASE_DATA: [u8; EDGE_CASE_LENGTH as usize] = [ 0xe0, 0x24, 0xa9, 0xba, 0xe0, 0x68, 0xa9, 0xba, 0x8a, 0x73, 0x41, 0x25, 0x12, 0x12, 0x1c, 0x28, 0x3b, 0xa6, 0x34, 0x8, 0x8, 0x7a, 0x38, 0x34, 0x2c, 0xe8, 0xf8, 0xd0, 0xef, 0x18, 0xc2, 0xc, 0x27, 0x1f, 0xb1, 0x83, 0x3c, 0x58, diff --git a/crates/ironrdp-tokio/src/reqwest.rs b/crates/ironrdp-tokio/src/reqwest.rs index 68060c27..b196c2aa 100644 --- a/crates/ironrdp-tokio/src/reqwest.rs +++ b/crates/ironrdp-tokio/src/reqwest.rs @@ -2,7 +2,7 @@ use core::future::Future; use core::net::{IpAddr, Ipv4Addr}; use core::pin::Pin; -use ironrdp_connector::{custom_err, ConnectorResult}; +use ironrdp_connector::{custom_err, general_err, ConnectorResult}; use reqwest::Client; use sspi::{Error, ErrorKind}; use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _}; @@ -62,7 +62,10 @@ impl ReqwestNetworkClient { .map_err(|e| Error::new(ErrorKind::NoAuthenticatingAuthority, format!("{e:?}"))) .map_err(|e| custom_err!("failed to send KDC request over TCP", e))?; - let mut buf = vec![0; len as usize + 4]; + let len = usize::try_from(len) + .map_err(|_| general_err!("invalid buffer length: out of range integral type conversion"))?; + + let mut buf = vec![0; len + 4]; buf[0..4].copy_from_slice(&(len.to_be_bytes())); stream diff --git a/crates/ironrdp-web/src/canvas.rs b/crates/ironrdp-web/src/canvas.rs index fcb7464e..96b9df50 100644 --- a/crates/ironrdp-web/src/canvas.rs +++ b/crates/ironrdp-web/src/canvas.rs @@ -1,5 +1,6 @@ use core::num::NonZeroU32; +use anyhow::Context as _; use ironrdp::pdu::geometry::{InclusiveRectangle, Rectangle as _}; use softbuffer::{NoDisplayHandle, NoWindowHandle}; use web_sys::HtmlCanvasElement; @@ -61,7 +62,7 @@ impl Canvas { let region_width_usize = usize::from(region_width); for dst_row in dst - .chunks_exact_mut(self.width.get() as usize) + .chunks_exact_mut(usize::try_from(self.width.get()).context("canvas width")?) .skip(region_top_usize) .take(region_height_usize) { diff --git a/crates/ironrdp-web/src/session.rs b/crates/ironrdp-web/src/session.rs index afcb387b..0c7378de 100644 --- a/crates/ironrdp-web/src/session.rs +++ b/crates/ironrdp-web/src/session.rs @@ -69,7 +69,7 @@ struct SessionBuilderInner { use_display_control: bool, enable_credssp: bool, - outbound_message_size_limit: Option, + outbound_message_size_limit: Option, } impl Default for SessionBuilderInner { @@ -216,8 +216,8 @@ impl iron_remote_desktop::SessionBuilder for SessionBuilder { |enable_credssp: bool| { self.0.borrow_mut().enable_credssp = enable_credssp }; |outbound_message_size_limit: f64| { let limit = if outbound_message_size_limit >= 0.0 && outbound_message_size_limit <= f64::from(u32::MAX) { - #[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss)] - { outbound_message_size_limit as u32 } + #[expect(clippy::as_conversions, clippy::cast_possible_truncation, clippy::cast_sign_loss)] + { outbound_message_size_limit as usize } } else { warn!(outbound_message_size_limit, "Invalid outbound message size limit; fallback to unlimited"); 0 // Fallback to no limit for invalid values. @@ -899,20 +899,20 @@ fn build_config( async fn writer_task( rx: mpsc::UnboundedReceiver>, rdp_writer: WriteHalf, - outbound_limit: Option, + outbound_limit: Option, ) { debug!("writer task started"); async fn inner( mut rx: mpsc::UnboundedReceiver>, mut rdp_writer: WriteHalf, - outbound_limit: Option, + outbound_limit: Option, ) -> anyhow::Result<()> { while let Some(frame) = rx.next().await { match outbound_limit { - Some(max_size) if frame.len() > max_size as usize => { + Some(max_size) if frame.len() > max_size => { // Send in chunks. - for chunk in frame.chunks(max_size as usize) { + for chunk in frame.chunks(max_size) { rdp_writer.write_all(chunk).await.context("couldn't write chunk")?; rdp_writer.flush().await.context("couldn't flush chunk")?; } @@ -1153,8 +1153,7 @@ where } } -#[expect(clippy::cast_sign_loss)] -#[expect(clippy::cast_possible_truncation)] +#[expect(clippy::as_conversions, clippy::cast_sign_loss, clippy::cast_possible_truncation)] fn f64_to_u16_saturating_cast(value: f64) -> u16 { value as u16 } diff --git a/crates/ironrdp/examples/server.rs b/crates/ironrdp/examples/server.rs index 35e8175d..94e4a9ac 100644 --- a/crates/ironrdp/examples/server.rs +++ b/crates/ironrdp/examples/server.rs @@ -364,19 +364,23 @@ fn generate_sine_wave(sample_rate: u32, frequency: f32, duration_ms: u64, phase: use core::f32::consts::PI; let total_samples = (u64::from(sample_rate) * duration_ms) / 1000; + + #[expect(clippy::as_conversions)] let delta_phase = 2.0 * PI * frequency / sample_rate as f32; + let amplitude = 32767.0; // Max amplitude for 16-bit audio - let capacity = (total_samples as usize) * 2; // 2 channels + let capacity = usize::try_from(total_samples).expect("u64-to-usize") * 2; // 2 channels let mut samples = Vec::with_capacity(capacity); for _ in 0..total_samples { let sample = (*phase).sin(); *phase += delta_phase; - // Wrap phase to maintain precision and avoid overflow + + // Wrap phase to maintain precision and avoid overflow. *phase %= 2.0 * PI; - #[expect(clippy::cast_possible_truncation)] + #[expect(clippy::as_conversions, clippy::cast_possible_truncation)] let sample_i16 = (sample * amplitude) as i16; // Write same sample to both channels (stereo) diff --git a/xtask/src/cov.rs b/xtask/src/cov.rs index f522db52..50e073ab 100644 --- a/xtask/src/cov.rs +++ b/xtask/src/cov.rs @@ -328,8 +328,11 @@ fn get_json_float(value: &tinyjson::JsonValue, key: &str) -> anyhow::Result } fn get_json_int(value: &tinyjson::JsonValue, key: &str) -> anyhow::Result { - // tinyjson does not expose any integers at all, so we need the f64 to u64 as casting - #[expect(clippy::cast_sign_loss)] - #[expect(clippy::cast_possible_truncation)] + #[expect( + clippy::as_conversions, + clippy::cast_sign_loss, + clippy::cast_possible_truncation, + reason = "tinyjson does not expose any integers at all, so we need the f64 to u64 as casting" + )] get_json_float(value, key).map(|value| value as u64) } From 79e71c4f90ea68b14fe45241c1cf3953027b22a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 14:12:28 -0500 Subject: [PATCH 27/51] build(deps): bump windows from 0.61.3 to 0.62.1 (#1010) Co-authored-by: Vladyslav Nikonov --- Cargo.lock | 107 +++++++++++++----- crates/ironrdp-client/Cargo.toml | 2 +- crates/ironrdp-cliprdr-native/Cargo.toml | 2 +- .../ironrdp-cliprdr-native/src/windows/mod.rs | 4 +- 4 files changed, 83 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 980260bb..cc00dc83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -379,7 +379,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -655,7 +655,7 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -2116,7 +2116,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.1", + "windows-core 0.62.2", ] [[package]] @@ -2463,7 +2463,7 @@ dependencies = [ "url", "uuid", "whoami", - "windows 0.61.3", + "windows 0.62.2", "winit", "x509-cert", ] @@ -2494,7 +2494,7 @@ dependencies = [ "ironrdp-cliprdr", "ironrdp-core", "tracing", - "windows 0.61.3", + "windows 0.62.2", ] [[package]] @@ -6209,11 +6209,23 @@ version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ - "windows-collections", + "windows-collections 0.2.0", "windows-core 0.61.2", - "windows-future", + "windows-future 0.2.1", "windows-link 0.1.3", - "windows-numerics", + "windows-numerics 0.2.0", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", ] [[package]] @@ -6225,6 +6237,15 @@ dependencies = [ "windows-core 0.61.2", ] +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core 0.62.2", +] + [[package]] name = "windows-core" version = "0.54.0" @@ -6250,15 +6271,15 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.62.1" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6844ee5416b285084d3d3fffd743b925a6c9385455f64f6d4fa3031c4c2749a9" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.2.0", - "windows-result 0.4.0", - "windows-strings 0.5.0", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] @@ -6269,14 +6290,25 @@ checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core 0.61.2", "windows-link 0.1.3", - "windows-threading", + "windows-threading 0.1.0", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", ] [[package]] name = "windows-implement" -version = "0.60.1" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb307e42a74fb6de9bf3a02d9712678b22399c87e6fa869d6dfcd8c1b7754e0" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", @@ -6285,9 +6317,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.59.2" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0abd1ddbc6964ac14db11c7213d6532ef34bd9aa042c2e5935f59d7908b46a5" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", @@ -6302,9 +6334,9 @@ checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-link" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-numerics" @@ -6316,6 +6348,16 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", +] + [[package]] name = "windows-registry" version = "0.5.3" @@ -6347,11 +6389,11 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -6365,11 +6407,11 @@ dependencies = [ [[package]] name = "windows-strings" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -6423,7 +6465,7 @@ version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" dependencies = [ - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -6478,7 +6520,7 @@ version = "0.53.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d42b7b7f66d2a06854650af09cfdf8713e427a439c97ad65a6375318033ac4b" dependencies = [ - "windows-link 0.2.0", + "windows-link 0.2.1", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -6498,6 +6540,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" diff --git a/crates/ironrdp-client/Cargo.toml b/crates/ironrdp-client/Cargo.toml index 4b4cffa8..5b6ab1b3 100644 --- a/crates/ironrdp-client/Cargo.toml +++ b/crates/ironrdp-client/Cargo.toml @@ -88,7 +88,7 @@ x509-cert = { version = "0.2", default-features = false, features = ["std"] } url = "2" [target.'cfg(windows)'.dependencies] -windows = { version = "0.61", features = ["Win32_Foundation"] } +windows = { version = "0.62", features = ["Win32_Foundation"] } [lints] workspace = true diff --git a/crates/ironrdp-cliprdr-native/Cargo.toml b/crates/ironrdp-cliprdr-native/Cargo.toml index 9e338410..bf4e5a20 100644 --- a/crates/ironrdp-cliprdr-native/Cargo.toml +++ b/crates/ironrdp-cliprdr-native/Cargo.toml @@ -21,7 +21,7 @@ ironrdp-core = { path = "../ironrdp-core", version = "0.1" } tracing = { version = "0.1", features = ["log"] } [target.'cfg(windows)'.dependencies] -windows = { version = "0.61", features = [ +windows = { version = "0.62", features = [ "Win32_Foundation", "Win32_Graphics_Gdi", "Win32_System_DataExchange", diff --git a/crates/ironrdp-cliprdr-native/src/windows/mod.rs b/crates/ironrdp-cliprdr-native/src/windows/mod.rs index 7051fcfb..48bf2dbc 100644 --- a/crates/ironrdp-cliprdr-native/src/windows/mod.rs +++ b/crates/ironrdp-cliprdr-native/src/windows/mod.rs @@ -162,7 +162,7 @@ impl WinClipboard { // SAFETY: low-level WinAPI call let atom = unsafe { RegisterClassA(&wc) }; if atom == 0 { - return Err(WinCliprdrError::from(Error::from_win32())); + return Err(WinCliprdrError::from(Error::from_thread())); } // SAFETY: low-level WinAPI call @@ -184,7 +184,7 @@ impl WinClipboard { }; if window.is_invalid() { - return Err(WinCliprdrError::from(Error::from_win32())); + return Err(WinCliprdrError::from(Error::from_thread())); } // Init clipboard processing for WinAPI event loop // From 966ba8a53e43a193271f40b9db80e45e495e2f24 Mon Sep 17 00:00:00 2001 From: Yuval Marcus Date: Mon, 17 Nov 2025 05:11:16 -0500 Subject: [PATCH 28/51] feat(ironrdp-tokio): add MovableTokioFramed for Send+!Sync context (#1033) The `ironrdp-tokio` crate currently provides the following two `Framed` implementations using the standard `tokio::io` traits: - `type TokioFramed = Framed>` where `S: Send + Sync + Unpin` - `type LocalTokioFramed = Framed>` where `S: Unpin` The former is meant for multi-threaded runtimes and the latter is meant for single-threaded runtimes. This PR adds a third `Framed` implementation: `pub type MovableTokioFramed = Framed>` where `S: Send + Unpin` This is a valid usecase as some implementations of the `tokio::io` traits are `Send` but `!Sync`. Without this new third type, consumers of `Framed` who have a `S: Send + !Sync` trait for their streams are forced to downgrade to `LocalTokioFramed` and do some hacky workaround with `tokio::task::spawn_blocking` since the defined associated futures, `ReadFut` and `WriteAllFut`, are neither `Send` nor `Sync`. --- crates/ironrdp-tokio/src/lib.rs | 63 +++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/crates/ironrdp-tokio/src/lib.rs b/crates/ironrdp-tokio/src/lib.rs index 010b2219..b188e84b 100644 --- a/crates/ironrdp-tokio/src/lib.rs +++ b/crates/ironrdp-tokio/src/lib.rs @@ -158,3 +158,66 @@ where }) } } + +pub type MovableTokioFramed = Framed>; + +pub struct MovableTokioStream { + inner: S, +} + +impl StreamWrapper for MovableTokioStream { + type InnerStream = S; + + fn from_inner(stream: Self::InnerStream) -> Self { + Self { inner: stream } + } + + fn into_inner(self) -> Self::InnerStream { + self.inner + } + + fn get_inner(&self) -> &Self::InnerStream { + &self.inner + } + + fn get_inner_mut(&mut self) -> &mut Self::InnerStream { + &mut self.inner + } +} + +impl FramedRead for MovableTokioStream +where + S: Send + Unpin + AsyncRead, +{ + type ReadFut<'read> + = Pin> + Send + 'read>> + where + Self: 'read; + + fn read<'a>(&'a mut self, buf: &'a mut BytesMut) -> Self::ReadFut<'a> { + use tokio::io::AsyncReadExt as _; + + Box::pin(async { self.inner.read_buf(buf).await }) + } +} + +impl FramedWrite for MovableTokioStream +where + S: Send + Unpin + AsyncWrite, +{ + type WriteAllFut<'write> + = Pin> + Send + 'write>> + where + Self: 'write; + + fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteAllFut<'a> { + use tokio::io::AsyncWriteExt as _; + + Box::pin(async { + self.inner.write_all(buf).await?; + self.inner.flush().await?; + + Ok(()) + }) + } +} From f2326ef046cc81fb0e8985f03382859085882e86 Mon Sep 17 00:00:00 2001 From: Yuval Marcus Date: Mon, 17 Nov 2025 05:13:47 -0500 Subject: [PATCH 29/51] fix(cliprdr)!: receiving a TemporaryDirectory PDU should not fail the svc (#1031) - Fixes the Cliprdr `SvcProcessor` impl. to support handling a `TemporaryDirectory` Clipboard PDU. - Removes `ClipboardError::UnimplementedPdu` since it is no longer used --- crates/ironrdp-cliprdr/src/lib.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/crates/ironrdp-cliprdr/src/lib.rs b/crates/ironrdp-cliprdr/src/lib.rs index 889ca92b..1c0d2143 100644 --- a/crates/ironrdp-cliprdr/src/lib.rs +++ b/crates/ironrdp-cliprdr/src/lib.rs @@ -28,16 +28,12 @@ pub type CliprdrSvcMessages = SvcProcessorMessages>; #[derive(Debug)] enum ClipboardError { - UnimplementedPdu { pdu: &'static str }, FormatListRejected, } impl core::fmt::Display for ClipboardError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - ClipboardError::UnimplementedPdu { pdu } => { - write!(f, "received clipboard PDU `{pdu}` is not implemented") - } ClipboardError::FormatListRejected => write!(f, "sent format list was rejected"), } } @@ -333,9 +329,10 @@ impl SvcProcessor for Cliprdr { self.backend.on_file_contents_response(response); Ok(Vec::new()) } - _ => self.handle_error_transition(ClipboardError::UnimplementedPdu { - pdu: pdu.message_name(), - }), + ClipboardPdu::TemporaryDirectory(_) => { + // do nothing + Ok(Vec::new()) + } } } From a70e01d9c5675a7dffd65eda7428537c8ad6a857 Mon Sep 17 00:00:00 2001 From: Yuval Marcus Date: Mon, 17 Nov 2025 07:36:31 -0500 Subject: [PATCH 30/51] fix(server): send TLS close_notify during graceful RDP disconnect (#1032) Add support for sending a proper TLS close_notify message when the RDP client initiates a graceful disconnect PDU. --- crates/ironrdp-server/src/server.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/crates/ironrdp-server/src/server.rs b/crates/ironrdp-server/src/server.rs index 9023a4cc..def121a5 100644 --- a/crates/ironrdp-server/src/server.rs +++ b/crates/ironrdp-server/src/server.rs @@ -21,7 +21,7 @@ use ironrdp_pdu::{decode_err, mcs, nego, rdp, Action, PduResult}; use ironrdp_svc::{server_encode_svc_messages, StaticChannelId, StaticChannelSet, SvcProcessor}; use ironrdp_tokio::{split_tokio_framed, unsplit_tokio_framed, FramedRead, FramedWrite, TokioFramed}; use rdpsnd::server::{RdpsndServer, RdpsndServerMessage}; -use tokio::io::{AsyncRead, AsyncWrite}; +use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt as _}; use tokio::net::{TcpListener, TcpStream}; use tokio::sync::{mpsc, oneshot, Mutex}; use tokio::task; @@ -357,7 +357,12 @@ impl RdpServer { .await?; } - self.accept_finalize(framed, acceptor).await?; + let framed = self.accept_finalize(framed, acceptor).await?; + debug!("Shutting down TLS connection"); + let (mut tls_stream, _) = framed.into_inner(); + if let Err(e) = tls_stream.shutdown().await { + debug!(?e, "TLS shutdown error"); + } } BeginResult::Continue(framed) => { @@ -954,7 +959,7 @@ impl RdpServer { } } - async fn accept_finalize(&mut self, mut framed: TokioFramed, mut acceptor: Acceptor) -> Result<()> + async fn accept_finalize(&mut self, mut framed: TokioFramed, mut acceptor: Acceptor) -> Result> where S: AsyncRead + AsyncWrite + Sync + Send + Unpin, { @@ -982,11 +987,12 @@ impl RdpServer { framed = unsplit_tokio_framed(reader, writer); continue; } - RunState::Disconnect => break, + RunState::Disconnect => { + let final_framed = unsplit_tokio_framed(reader, writer); + return Ok(final_framed); + } } } - - Ok(()) } pub fn set_credentials(&mut self, creds: Option) { From bfb0cae2f8599ae9f044d6fa04d3b5b550cbc76c Mon Sep 17 00:00:00 2001 From: Dion Gionet Mallet Date: Tue, 18 Nov 2025 01:24:12 -0500 Subject: [PATCH 31/51] ci(nuget): use Trusted Publishing auth (#1035) Issue: DEVOPS-3949 --- .github/workflows/nuget-publish.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nuget-publish.yml b/.github/workflows/nuget-publish.yml index d14cd9f3..2b9d7b0d 100644 --- a/.github/workflows/nuget-publish.yml +++ b/.github/workflows/nuget-publish.yml @@ -353,6 +353,8 @@ jobs: if: ${{ needs.preflight.outputs.dry-run == 'false' }} needs: [preflight, build-managed] runs-on: ubuntu-latest + permissions: + id-token: write steps: - name: Download NuGet package artifact @@ -361,6 +363,12 @@ jobs: name: ironrdp-nupkg path: package + - name: NuGet login (OIDC) + uses: NuGet/login@v1 + id: nuget-login + with: + user: ${{ secrets.NUGET_BOT_USERNAME }} + - name: Publish to nuget.org run: | $Files = Get-ChildItem -Recurse package/*.nupkg @@ -372,7 +380,7 @@ jobs: 'push', "$File", '--api-key', - '${{ secrets.NUGET_API_KEY }}', + '${{ steps.nuget-login.outputs.NUGET_API_KEY }}', '--source', 'https://api.nuget.org/v3/index.json', '--skip-duplicate' From 5bd319126d32fbd8e505508e27ab2b1a18a83d04 Mon Sep 17 00:00:00 2001 From: Allan Zhang <6740989+allan2@users.noreply.github.com> Date: Wed, 19 Nov 2025 09:08:42 -0500 Subject: [PATCH 32/51] build(deps): bump picky and sspi (#1028) This fixes build issues with some dependencies. --- Cargo.lock | 1566 +++++++++++++---------- crates/ironrdp-connector/Cargo.toml | 6 +- crates/ironrdp-connector/src/credssp.rs | 7 +- crates/ironrdp-tokio/Cargo.toml | 2 +- crates/ironrdp/Cargo.toml | 2 +- ffi/Cargo.toml | 2 +- 6 files changed, 907 insertions(+), 678 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc00dc83..7120ad75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,15 +18,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" -[[package]] -name = "addr2line" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" -dependencies = [ - "gimli", -] - [[package]] name = "adler2" version = "2.0.1" @@ -35,19 +26,19 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aead" -version = "0.5.2" +version = "0.6.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +checksum = "ac8202ab55fcbf46ca829833f347a82a2a4ce0596f0304ac322c2d100030cd56" dependencies = [ - "crypto-common", - "generic-array", + "crypto-common 0.2.0-rc.4", + "inout", ] [[package]] name = "aes" -version = "0.8.4" +version = "0.9.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +checksum = "7e713c57c2a2b19159e7be83b9194600d7e8eb3b7c2cd67e671adf47ce189a05" dependencies = [ "cfg-if", "cipher", @@ -56,9 +47,9 @@ dependencies = [ [[package]] name = "aes-gcm" -version = "0.10.3" +version = "0.11.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +checksum = "0686ba04dc80c816104c96cd7782b748f6ad58c5dd4ee619ff3258cf68e83d54" dependencies = [ "aead", "aes", @@ -70,11 +61,12 @@ dependencies = [ [[package]] name = "aes-kw" -version = "0.2.1" +version = "0.3.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fa2b352dcefb5f7f3a5fb840e02665d311d878955380515e4fd50095dd3d8c" +checksum = "02eaa2d54d0fad0116e4b1efb65803ea0bf059ce970a67cd49718d87e807cb51" dependencies = [ "aes", + "const-oid 0.10.1", ] [[package]] @@ -84,7 +76,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", "version_check", "zerocopy", @@ -92,9 +84,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -106,7 +98,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43" dependencies = [ "alsa-sys", - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "libc", ] @@ -128,7 +120,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.9.4", + "bitflags 2.10.0", "cc", "cesu8", "jni", @@ -165,9 +157,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -195,22 +187,22 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.10" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -331,6 +323,15 @@ dependencies = [ "syn", ] +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -345,9 +346,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.14.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879b6c89592deb404ba4dc0ae6b58ffd1795c78991cbb5b8bc441c48a070440d" +checksum = "5932a7d9d28b0d2ea34c6b3779d35e3dd6f6345317c34e73438c4f1f29144151" dependencies = [ "aws-lc-sys", "zeroize", @@ -355,38 +356,22 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.32.2" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b715a6010afb9e457ca2b7c9d2b9c344baa8baed7b38dc476034c171b32575" +checksum = "1826f2e4cfc2cd19ee53c42fbf68e2f81ec21108e0b7ecf6a71cf062137360fc" dependencies = [ "bindgen", "cc", "cmake", "dunce", "fs_extra", - "libloading", -] - -[[package]] -name = "backtrace" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-link 0.2.1", ] [[package]] name = "base16ct" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +checksum = "d8b59d472eab27ade8d770dcb11da7201c11234bef9f82ce7aa517be028d462b" [[package]] name = "base64" @@ -420,7 +405,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cexpr", "clang-sys", "itertools", @@ -463,9 +448,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.4" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "bitvec" @@ -489,12 +474,21 @@ dependencies = [ ] [[package]] -name = "block-padding" -version = "0.3.3" +name = "block-buffer" +version = "0.11.0-rc.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +checksum = "e9ef36a6fcdb072aa548f3da057640ec10859eb4e91ddf526ee648d50c76a949" dependencies = [ - "generic-array", + "hybrid-array", +] + +[[package]] +name = "block-padding" +version = "0.4.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e59c1aab3e6c5e56afe1b7e8650be9b5a791cb997bdea449194ae62e4bf8c73" +dependencies = [ + "hybrid-array", ] [[package]] @@ -555,15 +549,15 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "bytesize" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5c434ae3cf0089ca203e9019ebe529c47ff45cefe8af7c85ecb734ef541822f" +checksum = "c99fa31e08a43eaa5913ef68d7e01c37a2bdce6ed648168239ad33b7d30a9cd8" [[package]] name = "calloop" @@ -571,7 +565,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "log", "polling", "rustix 0.38.44", @@ -599,18 +593,18 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cbc" -version = "0.1.2" +version = "0.2.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +checksum = "5dbf9e5b071e9de872e32b73f485e8f644ff47c7011d95476733e7482ee3e5c3" dependencies = [ "cipher", ] [[package]] name = "cc" -version = "1.2.39" +version = "1.2.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f" +checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" dependencies = [ "find-msvc-tools", "jobserver", @@ -635,9 +629,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -687,11 +681,12 @@ dependencies = [ [[package]] name = "cipher" -version = "0.4.4" +version = "0.5.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +checksum = "1e12a13eb01ded5d32ee9658d94f553a19e804204f2dc811df69ab4d9e0cb8c7" dependencies = [ - "crypto-common", + "block-buffer 0.11.0-rc.5", + "crypto-common 0.2.0-rc.4", "inout", ] @@ -708,9 +703,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.50" +version = "4.5.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2cfd7bf8a6017ddaa4e32ffe7403d547790db06bd171c1c53926faab501623" +checksum = "aa8120877db0e5c011242f96806ce3c94e0737ab8108532a76a3300a01db2ab8" dependencies = [ "clap_builder", "clap_derive", @@ -718,9 +713,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.50" +version = "4.5.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4c05b9e80c5ccd3a7ef080ad7b6ba7d6fc00a985b8b157197075677c82c7a0" +checksum = "02576b399397b659c26064fbc92a75fede9d18ffd5f80ca1cd74ddab167016e1" dependencies = [ "anstream", "anstyle", @@ -742,9 +737,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "cmake" @@ -796,6 +791,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-oid" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dabb6555f92fb9ee4140454eb5dcd14c7960e1225c6d1a6cc361f032947713e" + [[package]] name = "convert_case" version = "0.7.1" @@ -850,7 +851,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation 0.10.1", "core-graphics-types 0.2.0", "foreign-types 0.5.0", @@ -874,7 +875,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation 0.10.1", "libc", ] @@ -970,6 +971,21 @@ dependencies = [ "itertools", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -1001,7 +1017,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "crossterm_winapi", "derive_more", "document-features", @@ -1028,38 +1044,40 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" -[[package]] -name = "crypto" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf1e6e5492f8f0830c37f301f6349e0dac8b2466e4fe89eef90e9eef906cd046" -dependencies = [ - "crypto-common", -] - [[package]] name = "crypto-bigint" -version = "0.5.5" +version = "0.7.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +checksum = "4113edbc9f68c0a64d5b911f803eb245d04bb812680fd56776411f69c670f3e0" dependencies = [ - "generic-array", - "rand_core 0.6.4", + "hybrid-array", + "num-traits", + "rand_core 0.9.3", + "serdect", "subtle", "zeroize", ] [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", - "rand_core 0.6.4", "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8235645834fbc6832939736ce2f2d08192652269e11010a6240f61b908a1c6" +dependencies = [ + "hybrid-array", + "rand_core 0.9.3", +] + [[package]] name = "crypto-mac" version = "0.11.0" @@ -1070,6 +1088,40 @@ dependencies = [ "subtle", ] +[[package]] +name = "crypto-primes" +version = "0.7.0-pre.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f2523fbb68811c8710829417ad488086720a6349e337c38d12fa81e09e50bf" +dependencies = [ + "crypto-bigint", + "libm", + "rand_core 0.9.3", +] + +[[package]] +name = "cryptoki" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "781357a7779a8e92ea985121bbf379a9adf0777f44ab6392efc6abd5aa9b67db" +dependencies = [ + "bitflags 1.3.2", + "cryptoki-sys", + "libloading", + "log", + "paste", + "secrecy", +] + +[[package]] +name = "cryptoki-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "753e27d860277930ae9f394c119c8c70303236aab0ffab1d51f3d207dbb2bc4b" +dependencies = [ + "libloading", +] + [[package]] name = "ctor-lite" version = "0.1.0" @@ -1078,9 +1130,9 @@ checksum = "1f791803201ab277ace03903de1594460708d2d54df6053f2d9e82f592b19e3b" [[package]] name = "ctr" -version = "0.9.2" +version = "0.10.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +checksum = "27e41d01c6f73b9330177f5cf782ae5b581b5f2c7840e298e0275ceee5001434" dependencies = [ "cipher", ] @@ -1093,14 +1145,14 @@ checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" [[package]] name = "curve25519-dalek" -version = "4.1.3" +version = "5.0.0-pre.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +checksum = "6f9200d1d13637f15a6acb71e758f64624048d85b31a5fdbfd8eca1e2687d0b7" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", - "digest", + "digest 0.11.0-rc.3", "fiat-crypto", "rustc_version", "subtle", @@ -1136,10 +1188,21 @@ version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ - "const-oid", + "const-oid 0.9.6", "der_derive", "flagset", - "pem-rfc7468", + "pem-rfc7468 0.7.0", + "zeroize", +] + +[[package]] +name = "der" +version = "0.8.0-rc.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d8dd2f26c86b27a2a8ea2767ec7f9df7a89516e4794e54ac01ee618dda3aa4" +dependencies = [ + "const-oid 0.10.1", + "pem-rfc7468 1.0.0-rc.3", "zeroize", ] @@ -1169,9 +1232,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", ] @@ -1210,9 +1273,9 @@ dependencies = [ [[package]] name = "des" -version = "0.8.1" +version = "0.9.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" +checksum = "3f51594a70805988feb1c85495ddec0c2052e4fbe59d9c0bb7f94bfc164f4f90" dependencies = [ "cipher", ] @@ -1229,9 +1292,19 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", + "block-buffer 0.10.4", + "crypto-common 0.1.7", +] + +[[package]] +name = "digest" +version = "0.11.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac89f8a64533a9b0eaa73a68e424db0fb1fd6271c74cc0125336a05f090568d" +dependencies = [ + "block-buffer 0.11.0-rc.5", + "const-oid 0.10.1", + "crypto-common 0.2.0-rc.4", "subtle", ] @@ -1278,8 +1351,8 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.2", + "bitflags 2.10.0", + "objc2 0.6.3", ] [[package]] @@ -1310,9 +1383,9 @@ dependencies = [ [[package]] name = "document-features" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" dependencies = [ "litrs", ] @@ -1335,7 +1408,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "bytemuck", "drm-ffi", "drm-fourcc", @@ -1382,23 +1455,24 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "ecdsa" -version = "0.16.9" +version = "0.17.0-rc.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +checksum = "b4ab355ec063f7a110eb627471058093aba00eb7f4e70afbd15e696b79d1077b" dependencies = [ - "der", - "digest", + "der 0.8.0-rc.9", + "digest 0.11.0-rc.3", "elliptic-curve", "rfc6979", "signature", - "spki", + "spki 0.8.0-rc.4", + "zeroize", ] [[package]] name = "ed25519" -version = "2.2.3" +version = "3.0.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +checksum = "9ef49c0b20c0ad088893ad2a790a29c06a012b3f05bcfc66661fd22a94b32129" dependencies = [ "pkcs8", "signature", @@ -1406,14 +1480,13 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.2.0" +version = "3.0.0-pre.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +checksum = "ad207ed88a133091f83224265eac21109930db09bedcad05d5252f2af2de20a1" dependencies = [ "curve25519-dalek", "ed25519", - "rand_core 0.6.4", - "serde", + "rand_core 0.9.3", "sha2", "subtle", "zeroize", @@ -1427,20 +1500,21 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "elliptic-curve" -version = "0.13.8" +version = "0.14.0-rc.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +checksum = "2e3be87c458d756141f3b6ee188828132743bf90c7d14843e2835d6443e5fb03" dependencies = [ "base16ct", "crypto-bigint", - "digest", + "digest 0.11.0-rc.3", "ff", - "generic-array", "group", "hkdf", - "pem-rfc7468", + "hybrid-array", + "once_cell", + "pem-rfc7468 1.0.0-rc.3", "pkcs8", - "rand_core 0.6.4", + "rand_core 0.9.3", "sec1", "subtle", "zeroize", @@ -1485,7 +1559,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -1515,11 +1589,11 @@ dependencies = [ [[package]] name = "ff" -version = "0.13.1" +version = "0.14.0-pre.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +checksum = "d42dd26f5790eda47c1a2158ea4120e32c35ddc9a7743c98a292accc01b54ef3" dependencies = [ - "rand_core 0.6.4", + "rand_core 0.9.3", "subtle", ] @@ -1544,15 +1618,15 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.9" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" [[package]] name = "find-msvc-tools" -version = "0.1.2" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "flagset" @@ -1562,11 +1636,12 @@ checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" [[package]] name = "flate2" -version = "1.1.2" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", + "libz-sys", "miniz_oxide", ] @@ -1751,17 +1826,16 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", - "zeroize", ] [[package]] name = "gethostname" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc257fdb4038301ce4b9cd1b3b51704509692bb3ff716a410cbd07925d9dae55" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ "rustix 1.1.2", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] @@ -1773,40 +1847,33 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi", - "wasi 0.14.7+wasi-0.2.4", + "wasip2", "wasm-bindgen", ] [[package]] name = "ghash" -version = "0.5.1" +version = "0.6.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +checksum = "4f88107cb02ed63adcc4282942e60c4d09d80208d33b360ce7c729ce6dae1739" dependencies = [ - "opaque-debug", "polyval", ] -[[package]] -name = "gimli" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" - [[package]] name = "glob" version = "0.3.3" @@ -1858,12 +1925,12 @@ dependencies = [ [[package]] name = "group" -version = "0.13.0" +version = "0.14.0-pre.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +checksum = "1ff6a0b2dd4b981b1ae9e3e6830ab146771f3660d31d57bafd9018805a91b0f1" dependencies = [ "ff", - "rand_core 0.6.4", + "rand_core 0.9.3", "subtle", ] @@ -1888,12 +1955,22 @@ dependencies = [ [[package]] name = "half" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", + "zerocopy", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", ] [[package]] @@ -1902,6 +1979,19 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "spin", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.5.0" @@ -1922,9 +2012,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hickory-proto" -version = "0.24.4" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92652067c9ce6f66ce53cc38d1169daa36e6e7eb7dd3b63b5103bd9d97117248" +checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" dependencies = [ "async-trait", "cfg-if", @@ -1936,8 +2026,9 @@ dependencies = [ "idna", "ipnet", "once_cell", - "rand 0.8.5", - "thiserror 1.0.69", + "rand 0.9.2", + "ring", + "thiserror 2.0.17", "tinyvec", "tokio", "tracing", @@ -1946,41 +2037,41 @@ dependencies = [ [[package]] name = "hickory-resolver" -version = "0.24.4" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbb117a1ca520e111743ab2f6688eddee69db4e0ea242545a604dce8a66fd22e" +checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a" dependencies = [ "cfg-if", "futures-util", "hickory-proto", "ipconfig", - "lru-cache", + "moka", "once_cell", "parking_lot", - "rand 0.8.5", + "rand 0.9.2", "resolv-conf", "smallvec", - "thiserror 1.0.69", + "thiserror 2.0.17", "tokio", "tracing", ] [[package]] name = "hkdf" -version = "0.12.4" +version = "0.13.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +checksum = "d8ef30358b03ca095a5b910547f4f8d4b9f163e4057669c5233ef595b1ecf008" dependencies = [ "hmac", ] [[package]] name = "hmac" -version = "0.12.1" +version = "0.13.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +checksum = "a3fd4dc94c318c1ede8a2a48341c250d6ddecd3ba793da2820301a9f92417ad9" dependencies = [ - "digest", + "digest 0.11.0-rc.3", ] [[package]] @@ -2024,10 +2115,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] -name = "hyper" -version = "1.7.0" +name = "hybrid-array" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "f471e0a81b2f90ffc0cb2f951ae04da57de8baa46fa99112b062a5173a5088d0" +dependencies = [ + "subtle", + "typenum", + "zeroize", +] + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", @@ -2080,9 +2182,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" dependencies = [ "base64", "bytes", @@ -2096,12 +2198,12 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.0", + "socket2 0.6.1", "system-configuration", "tokio", "tower-service", "tracing", - "windows-registry", + "windows-registry 0.6.1", ] [[package]] @@ -2130,9 +2232,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -2143,9 +2245,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -2156,11 +2258,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -2171,42 +2272,38 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -2237,9 +2334,9 @@ dependencies = [ [[package]] name = "image" -version = "0.25.8" +version = "0.25.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "529feb3e6769d234375c4cf1ee2ce713682b8e76538cb13f9fc23e1400a591e7" +checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" dependencies = [ "bytemuck", "byteorder-lite", @@ -2250,9 +2347,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.4" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", "hashbrown", @@ -2260,12 +2357,12 @@ dependencies = [ [[package]] name = "inout" -version = "0.1.4" +version = "0.2.0-rc.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +checksum = "1603f76010ff924b616c8f44815a42eb10fb0b93d308b41deaa8da6d4251fd4b" dependencies = [ "block-padding", - "generic-array", + "hybrid-array", ] [[package]] @@ -2274,7 +2371,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2628910d0114e9139056161d8644a2026be7b117f8498943f9437748b04c9e0a" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "crossterm", "dyn-clone", "fuzzy-matcher", @@ -2282,17 +2379,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags 2.9.4", - "cfg-if", - "libc", -] - [[package]] name = "ipconfig" version = "0.3.2" @@ -2313,9 +2399,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" dependencies = [ "memchr", "serde", @@ -2382,7 +2468,7 @@ dependencies = [ name = "ironrdp-ainput" version = "0.4.0" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "ironrdp-core", "ironrdp-dvc", "num-derive", @@ -2472,7 +2558,7 @@ dependencies = [ name = "ironrdp-cliprdr" version = "0.4.0" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "ironrdp-core", "ironrdp-pdu", "ironrdp-svc", @@ -2591,7 +2677,7 @@ name = "ironrdp-graphics" version = "0.6.0" dependencies = [ "bit_field", - "bitflags 2.9.4", + "bitflags 2.10.0", "bitvec", "bmp", "bytemuck", @@ -2618,7 +2704,7 @@ name = "ironrdp-mstsgu" version = "0.0.1" dependencies = [ "base64", - "bitflags 2.9.4", + "bitflags 2.10.0", "futures-util", "http-body-util", "hyper", @@ -2638,19 +2724,19 @@ name = "ironrdp-pdu" version = "0.6.0" dependencies = [ "bit_field", - "bitflags 2.9.4", + "bitflags 2.10.0", "byteorder", "der-parser", "expect-test", "ironrdp-core", "ironrdp-error", - "md-5", + "md-5 0.10.6", "num-bigint", "num-derive", "num-integer", "num-traits", - "pkcs1", - "sha1", + "pkcs1 0.7.5", + "sha1 0.10.6", "tap", "thiserror 2.0.17", "x509-cert", @@ -2671,14 +2757,14 @@ dependencies = [ name = "ironrdp-rdcleanpath" version = "0.2.1" dependencies = [ - "der", + "der 0.7.10", ] [[package]] name = "ironrdp-rdpdr" version = "0.4.1" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "ironrdp-core", "ironrdp-error", "ironrdp-pdu", @@ -2709,7 +2795,7 @@ dependencies = [ name = "ironrdp-rdpsnd" version = "0.6.0" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "ironrdp-core", "ironrdp-pdu", "ironrdp-svc", @@ -2784,7 +2870,7 @@ version = "0.0.0" name = "ironrdp-svc" version = "0.5.0" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "ironrdp-core", "ironrdp-pdu", ] @@ -2869,7 +2955,7 @@ dependencies = [ "futures-channel", "futures-util", "getrandom 0.2.16", - "getrandom 0.3.3", + "getrandom 0.3.4", "gloo-net", "gloo-timers", "iron-remote-desktop", @@ -2899,9 +2985,27 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "iso7816" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3c7e91da489667bb054f9cd2f1c60cc2ac4478a899f403d11dbc62189215b0" +dependencies = [ + "heapless", +] + +[[package]] +name = "iso7816-tlv" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7660d28d24a831d690228a275d544654a30f3b167a8e491cf31af5fe5058b546" +dependencies = [ + "untrusted", +] [[package]] name = "itertools" @@ -2946,15 +3050,15 @@ version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "libc", ] [[package]] name = "js-sys" -version = "0.3.81" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" dependencies = [ "once_cell", "wasm-bindgen", @@ -2962,9 +3066,9 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.5" +version = "0.2.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +checksum = "3d546793a04a1d3049bd192856f804cfe96356e2cf36b54b4e575155babe9f41" dependencies = [ "cpufeatures", ] @@ -2974,24 +3078,21 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] [[package]] name = "libc" -version = "0.2.176" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libloading" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-targets 0.53.4", + "windows-link 0.2.1", ] [[package]] @@ -3017,16 +3118,21 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "libc", - "redox_syscall 0.5.17", + "redox_syscall 0.5.18", ] [[package]] -name = "linked-hash-map" -version = "0.5.6" +name = "libz-sys" +version = "1.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] [[package]] name = "linux-raw-sys" @@ -3048,23 +3154,22 @@ checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "litrs" -version = "0.4.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] @@ -3074,15 +3179,6 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" -[[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "lru-slab" version = "0.1.2" @@ -3114,7 +3210,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if", - "digest", + "digest 0.10.7", +] + +[[package]] +name = "md-5" +version = "0.11.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9ec86664728010f574d67ef01aec964e6f1299241a3402857c1a8a390a62478" +dependencies = [ + "cfg-if", + "digest 0.11.0-rc.3", ] [[package]] @@ -3123,7 +3229,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da5ac363534dce5fabf69949225e174fbf111a498bf0ff794c8ea1fba9f3dda" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -3134,9 +3240,9 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memmap2" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" +checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" dependencies = [ "libc", ] @@ -3159,21 +3265,39 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ "libc", "log", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "moka" +version = "0.12.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8261cd88c312e0004c1d51baad2980c66528dfdb2bee62003e643a4d8f86b077" +dependencies = [ + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "equivalent", + "parking_lot", + "portable-atomic", + "rustc_version", + "smallvec", + "tagptr", + "uuid", ] [[package]] name = "moxcms" -version = "0.7.5" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd32fa8935aeadb8a8a6b6b351e40225570a37c43de67690383d87ef170cd08" +checksum = "0fbdd3d7436f8b5e892b8b7ea114271ff0fa00bc5acae845d53b07d498616ef6" dependencies = [ "num-traits", "pxfm", @@ -3202,7 +3326,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "jni-sys", "log", "ndk-sys", @@ -3232,7 +3356,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "cfg_aliases", "libc", @@ -3250,11 +3374,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.50.1" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3267,24 +3391,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "serde", - "smallvec", - "zeroize", -] - [[package]] name = "num-conv" version = "0.1.0" @@ -3311,17 +3417,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -3329,14 +3424,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", - "libm", ] [[package]] name = "num_enum" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" dependencies = [ "num_enum_derive", "rustversion", @@ -3344,9 +3438,9 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -3372,9 +3466,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561f357ba7f3a2a61563a186a163d0a3a5247e1089524a3981d49adb775078bc" +checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" dependencies = [ "objc2-encode", ] @@ -3385,7 +3479,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "block2", "libc", "objc2 0.5.2", @@ -3397,17 +3491,17 @@ dependencies = [ [[package]] name = "objc2-audio-toolbox" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10cbe18d879e20a4aea544f8befe38bcf52255eb63d3f23eca2842f3319e4c07" +checksum = "6948501a91121d6399b79abaa33a8aa4ea7857fe019f341b8c23ad6e81b79b08" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "libc", - "objc2 0.6.2", + "objc2 0.6.3", "objc2-core-audio", "objc2-core-audio-types", "objc2-core-foundation", - "objc2-foundation 0.3.1", + "objc2-foundation 0.3.2", ] [[package]] @@ -3416,7 +3510,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "block2", "objc2 0.5.2", "objc2-core-location", @@ -3436,24 +3530,24 @@ dependencies = [ [[package]] name = "objc2-core-audio" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca44961e888e19313b808f23497073e3f6b3c22bb485056674c8b49f3b025c82" +checksum = "e1eebcea8b0dbff5f7c8504f3107c68fc061a3eb44932051c8cf8a68d969c3b2" dependencies = [ "dispatch2", - "objc2 0.6.2", + "objc2 0.6.3", "objc2-core-audio-types", "objc2-core-foundation", ] [[package]] name = "objc2-core-audio-types" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f1cc99bb07ad2ddb6527ddf83db6a15271bb036b3eb94b801cd44fdc666ee1" +checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c" dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.2", + "bitflags 2.10.0", + "objc2 0.6.3", ] [[package]] @@ -3462,7 +3556,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3470,13 +3564,13 @@ dependencies = [ [[package]] name = "objc2-core-foundation" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "dispatch2", - "objc2 0.6.2", + "objc2 0.6.3", ] [[package]] @@ -3515,7 +3609,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "block2", "dispatch", "libc", @@ -3524,11 +3618,11 @@ dependencies = [ [[package]] name = "objc2-foundation" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "objc2 0.6.2", + "objc2 0.6.3", ] [[package]] @@ -3549,7 +3643,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3561,7 +3655,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3584,7 +3678,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "block2", "objc2 0.5.2", "objc2-cloud-kit", @@ -3616,22 +3710,13 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "block2", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", ] -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", -] - [[package]] name = "oid" version = "0.2.1" @@ -3646,12 +3731,16 @@ name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +dependencies = [ + "critical-section", + "portable-atomic", +] [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "oorandom" @@ -3659,19 +3748,13 @@ version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - [[package]] name = "openssl" -version = "0.10.73" +version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "foreign-types 0.3.2", "libc", @@ -3699,9 +3782,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.109" +version = "0.9.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", @@ -3720,9 +3803,9 @@ dependencies = [ [[package]] name = "orbclient" -version = "0.3.48" +version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba0b26cec2e24f08ed8bb31519a9333140a6599b867dac464bb150bdb796fd43" +checksum = "247ad146e19b9437f8604c21f8652423595cf710ad108af40e77d3ae6e96b827" dependencies = [ "libredox", ] @@ -3738,47 +3821,51 @@ dependencies = [ [[package]] name = "p256" -version = "0.13.2" +version = "0.14.0-pre.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +checksum = "81b374901df34ee468167a58e2a49e468cb059868479cafebeb804f6b855423d" dependencies = [ "ecdsa", "elliptic-curve", + "primefield", "primeorder", "sha2", ] [[package]] name = "p384" -version = "0.13.1" +version = "0.14.0-pre.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +checksum = "701032b3730df6b882496d6cee8221de0ce4bc11ddc64e6d89784aa5b8a6de30" dependencies = [ "ecdsa", "elliptic-curve", + "fiat-crypto", + "primefield", "primeorder", "sha2", ] [[package]] name = "p521" -version = "0.13.3" +version = "0.14.0-pre.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" +checksum = "40ba29c2906eb5c89a8c411c4f11243ee4e5517ee7d71d9a13fedc877a6057b1" dependencies = [ "base16ct", "ecdsa", "elliptic-curve", + "primefield", "primeorder", - "rand_core 0.6.4", + "rand_core 0.9.3", "sha2", ] [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -3786,15 +3873,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.17", + "redox_syscall 0.5.18", "smallvec", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] @@ -3805,13 +3892,13 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pbkdf2" -version = "0.12.2" +version = "0.13.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +checksum = "ca3fc18bb4460ac250ba6b75dfa7cf9d0b2273e3e623f660bd6ce2c3e902342e" dependencies = [ - "digest", + "digest 0.11.0-rc.3", "hmac", - "sha1", + "sha1 0.11.0-rc.2", ] [[package]] @@ -3823,6 +3910,15 @@ dependencies = [ "base64ct", ] +[[package]] +name = "pem-rfc7468" +version = "1.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8e58fab693c712c0d4e88f8eb3087b6521d060bcaf76aeb20cb192d809115ba" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -3831,40 +3927,69 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "picky" -version = "7.0.0-rc.17" +version = "7.0.0-rc.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33807ce79d4b14a8918e968a8606e5142ddc6aec933acef79de0bd769cae5fb1" +checksum = "4cdc52be663aebd70d7006ae305c87eb32a2b836d6c2f26f7e384f845d80b621" dependencies = [ + "aead", "aes", "aes-gcm", "aes-kw", "base64", + "block-buffer 0.11.0-rc.5", + "block-padding", "cbc", + "cipher", + "crypto-bigint", + "crypto-common 0.2.0-rc.4", + "crypto-primes", + "ctr", + "curve25519-dalek", + "der 0.8.0-rc.9", "des", - "digest", + "digest 0.11.0-rc.3", + "ecdsa", + "ed25519", "ed25519-dalek", + "elliptic-curve", + "ff", + "ghash", + "group", "hex", + "hkdf", "hmac", "http", - "md-5", - "num-bigint-dig", + "inout", + "keccak", + "md-5 0.11.0-rc.2", "p256", "p384", "p521", "pbkdf2", + "pem-rfc7468 1.0.0-rc.3", "picky-asn1", "picky-asn1-der", "picky-asn1-x509", - "rand 0.8.5", - "rand_core 0.6.4", + "pkcs1 0.8.0-rc.4", + "pkcs8", + "polyval", + "primefield", + "primeorder", + "rand 0.9.2", + "rand_core 0.9.3", "rc2", + "rfc6979", "rsa", + "sec1", "serde", "serde_json", - "sha1", + "sha1 0.11.0-rc.2", "sha2", "sha3", - "thiserror 1.0.69", + "signature", + "spki 0.8.0-rc.4", + "thiserror 2.0.17", + "universal-hash", "x25519-dalek", "zeroize", ] @@ -3884,9 +4009,9 @@ dependencies = [ [[package]] name = "picky-asn1-der" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c9c33310628f2e1758bbde11b16713505e188a859ab7990f98eb015b943bf5" +checksum = "b491eb61603cba1ad5c6be0269883538f8d74136c35e3641a840fb0fbcd41efc" dependencies = [ "picky-asn1", "serde", @@ -3895,12 +4020,12 @@ dependencies = [ [[package]] name = "picky-asn1-x509" -version = "0.14.6" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d493f73cf052073ca1fe38666f74c2396987aa6ea660e77dd624cc6c8f60389e" +checksum = "c97cd14d567a17755910fa8718277baf39d08682a980b1b1a4b4da7d0bc61a04" dependencies = [ "base64", - "num-bigint-dig", + "crypto-bigint", "oid", "picky-asn1", "picky-asn1-der", @@ -3911,26 +4036,31 @@ dependencies = [ [[package]] name = "picky-krb" -version = "0.11.1" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e78a55491723b0a10bc2c02709a8d92d74ef674fe1b569cb4a08bac3d105487" +checksum = "14b13eb1a97b2293277b475f07d0c36c33579e2e71f852557015addcd95f8892" dependencies = [ "aes", + "block-buffer 0.11.0-rc.5", + "block-padding", "byteorder", "cbc", - "crypto", + "cipher", + "crypto-bigint", + "crypto-common 0.2.0-rc.4", "des", + "digest 0.11.0-rc.3", "hmac", - "num-bigint-dig", + "inout", "oid", "pbkdf2", "picky-asn1", "picky-asn1-der", "picky-asn1-x509", - "rand 0.8.5", + "rand 0.9.2", "serde", - "sha1", - "thiserror 1.0.69", + "sha1 0.11.0-rc.2", + "thiserror 2.0.17", "uuid", ] @@ -3978,19 +4108,28 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ - "der", - "pkcs8", - "spki", + "der 0.7.10", + "spki 0.7.3", +] + +[[package]] +name = "pkcs1" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986d2e952779af96ea048f160fd9194e1751b4faea78bcf3ceb456efe008088e" +dependencies = [ + "der 0.8.0-rc.9", + "spki 0.8.0-rc.4", ] [[package]] name = "pkcs8" -version = "0.10.2" +version = "0.11.0-rc.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +checksum = "93eac55f10aceed84769df670ea4a32d2ffad7399400d41ee1c13b1cd8e1b478" dependencies = [ - "der", - "spki", + "der 0.8.0-rc.9", + "spki 0.8.0-rc.4", ] [[package]] @@ -4033,7 +4172,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "crc32fast", "fdeflate", "flate2", @@ -4051,21 +4190,26 @@ dependencies = [ "hermit-abi", "pin-project-lite", "rustix 1.1.2", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] name = "polyval" -version = "0.6.2" +version = "0.7.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +checksum = "1ffd40cc99d0fbb02b4b3771346b811df94194bc103983efa0203c8893755085" dependencies = [ "cfg-if", "cpufeatures", - "opaque-debug", "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + [[package]] name = "portpicker" version = "0.1.1" @@ -4077,9 +4221,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -4120,10 +4264,23 @@ dependencies = [ ] [[package]] -name = "primeorder" -version = "0.13.6" +name = "primefield" +version = "0.14.0-pre.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +checksum = "d7fcd4a163053332fd93f39b81c133e96a98567660981654579c90a99062fbf5" +dependencies = [ + "crypto-bigint", + "ff", + "rand_core 0.9.3", + "subtle", + "zeroize", +] + +[[package]] +name = "primeorder" +version = "0.14.0-pre.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c36e8766fcd270fa9c665b9dc364f570695f5a59240949441b077a397f15b74" dependencies = [ "elliptic-curve", ] @@ -4145,23 +4302,22 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb0be07becd10686a0bb407298fb425360a5c44a663774406340c59a22de4ce" +checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.9.4", - "lazy_static", + "bitflags 2.10.0", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", @@ -4174,9 +4330,9 @@ dependencies = [ [[package]] name = "pxfm" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83f9b339b02259ada5c0f4a389b7fb472f933aa17ce176fd2ad98f28bb401fde" +checksum = "a3cbdf373972bf78df4d3b518d07003938e2c7d1fb5891e55f9cb6df57009d84" dependencies = [ "num-traits", ] @@ -4218,7 +4374,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.0", + "socket2 0.6.1", "thiserror 2.0.17", "tokio", "tracing", @@ -4232,7 +4388,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", - "getrandom 0.3.3", + "getrandom 0.3.4", "lru-slab", "rand 0.9.2", "ring", @@ -4255,16 +4411,16 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.0", + "socket2 0.6.1", "tracing", "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.41" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -4337,7 +4493,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", ] [[package]] @@ -4377,9 +4533,9 @@ dependencies = [ [[package]] name = "rc2" -version = "0.8.1" +version = "0.9.0-pre.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62c64daa8e9438b84aaae55010a93f396f8e60e3911590fcba770d04643fc1dd" +checksum = "b03621ac292cc723def9e0fd0eb9573b1df8d6a9ee7ad637fe94dfc153705f3c" dependencies = [ "cipher", ] @@ -4395,18 +4551,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.17" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", ] [[package]] name = "regex" -version = "1.11.3" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -4416,9 +4572,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.11" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -4427,9 +4583,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "relative-path" @@ -4492,15 +4648,15 @@ dependencies = [ [[package]] name = "resolv-conf" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b3789b30bd25ba102de4beabd95d21ac45b69b1be7d14522bab988c526d6799" +checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" [[package]] name = "rfc6979" -version = "0.4.0" +version = "0.5.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +checksum = "d369f9c4f79388704648e7bcb92749c0d6cf4397039293a9b747694fa4fb4bae" dependencies = [ "hmac", "subtle", @@ -4531,21 +4687,20 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.8" +version = "0.10.0-rc.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +checksum = "bf8955ab399f6426998fde6b76ae27233cce950705e758a6c17afd2f6d0e5d52" dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", + "const-oid 0.10.1", + "crypto-bigint", + "crypto-primes", + "digest 0.11.0-rc.3", + "pkcs1 0.8.0-rc.4", "pkcs8", - "rand_core 0.6.4", - "sha1", + "rand_core 0.9.3", + "sha1 0.11.0-rc.2", "signature", - "spki", + "spki 0.8.0-rc.4", "subtle", "zeroize", ] @@ -4580,12 +4735,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - [[package]] name = "rustc-hash" version = "2.1.1" @@ -4616,7 +4765,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -4629,18 +4778,18 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.32" +version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "aws-lc-rs", "log", @@ -4654,9 +4803,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -4675,9 +4824,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ "web-time", "zeroize", @@ -4685,9 +4834,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.7" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "aws-lc-rs", "ring", @@ -4703,9 +4852,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-fork" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" dependencies = [ "fnv", "quick-error", @@ -4734,7 +4883,7 @@ version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -4764,25 +4913,33 @@ dependencies = [ [[package]] name = "sec1" -version = "0.7.3" +version = "0.8.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +checksum = "1dff52f6118bc9f0ac974a54a639d499ac26a6cad7a6e39bc0990c19625e793b" dependencies = [ "base16ct", - "der", - "generic-array", - "pkcs8", + "der 0.8.0-rc.9", + "hybrid-array", "subtle", "zeroize", ] +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + [[package]] name = "security-framework" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -4795,7 +4952,7 @@ version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -4873,9 +5030,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" dependencies = [ "serde_core", ] @@ -4892,6 +5049,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serdect" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3ef0e35b322ddfaecbc60f34ab448e157e48531288ee49fafbb053696b8ffe2" +dependencies = [ + "base16ct", + "serde", +] + [[package]] name = "sha1" version = "0.10.6" @@ -4900,27 +5067,38 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", +] + +[[package]] +name = "sha1" +version = "0.11.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e046edf639aa2e7afb285589e5405de2ef7e61d4b0ac1e30256e3eab911af9" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.11.0-rc.3", ] [[package]] name = "sha2" -version = "0.10.9" +version = "0.11.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +checksum = "d1e3878ab0f98e35b2df35fe53201d088299b41a6bb63e3e34dada2ac4abd924" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.11.0-rc.3", ] [[package]] name = "sha3" -version = "0.10.8" +version = "0.11.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +checksum = "2103ca0e6f4e9505eae906de5e5883e06fc3b2232fb5d6914890c7bbcb62f478" dependencies = [ - "digest", + "digest 0.11.0-rc.3", "keccak", ] @@ -4951,9 +5129,9 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" dependencies = [ "libc", "mio", @@ -4971,12 +5149,12 @@ dependencies = [ [[package]] name = "signature" -version = "2.2.0" +version = "3.0.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +checksum = "fc280a6ff65c79fbd6622f64d7127f32b85563bca8c53cd2e9141d6744a9056d" dependencies = [ - "digest", - "rand_core 0.6.4", + "digest 0.11.0-rc.3", + "rand_core 0.9.3", ] [[package]] @@ -5003,7 +5181,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "calloop", "calloop-wayland-source", "cursor-icon", @@ -5043,12 +5221,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -5071,7 +5249,7 @@ dependencies = [ "objc2-foundation 0.2.2", "objc2-quartz-core", "raw-window-handle", - "redox_syscall 0.5.17", + "redox_syscall 0.5.18", "rustix 0.38.44", "tiny-xlib", "wasm-bindgen", @@ -5088,6 +5266,9 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] name = "spki" @@ -5096,62 +5277,94 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der", + "der 0.7.10", +] + +[[package]] +name = "spki" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8baeff88f34ed0691978ec34440140e1572b68c7dd4a495fd14a3dc1944daa80" +dependencies = [ + "base64ct", + "der 0.8.0-rc.9", ] [[package]] name = "sspi" -version = "0.16.1" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523f6a99e26c1e6476a424d54bbda5354a01ee7f18b9d93dc48a8fd45ae8189b" +checksum = "70f8d436177e1f17d2efb8ec1b661068ad04fd282544dfd8369522168300a82e" dependencies = [ "async-dnssd", "async-recursion", - "bitflags 2.9.4", + "bitflags 2.10.0", + "block-buffer 0.11.0-rc.5", "byteorder", "cfg-if", + "crypto-bigint", + "crypto-common 0.2.0-rc.4", "crypto-mac", + "crypto-primes", + "cryptoki", + "curve25519-dalek", + "der 0.8.0-rc.9", + "digest 0.11.0-rc.3", + "ed25519-dalek", + "ff", "futures", + "getrandom 0.3.4", + "group", + "hickory-proto", "hickory-resolver", "hmac", - "lazy_static", - "md-5", + "md-5 0.11.0-rc.2", "md4", - "num-bigint-dig", "num-derive", "num-traits", "oid", + "p256", + "p384", + "p521", + "pem-rfc7468 1.0.0-rc.3", "picky", "picky-asn1", "picky-asn1-der", "picky-asn1-x509", "picky-krb", + "pkcs1 0.8.0-rc.4", + "pkcs8", "portpicker", - "rand 0.8.5", + "primefield", + "primeorder", + "rand 0.9.2", "reqwest", "rsa", "rustls", "rustls-native-certs", "serde", "serde_derive", - "sha1", + "sha1 0.11.0-rc.2", "sha2", + "signature", + "spki 0.8.0-rc.4", "time", "tokio", "tracing", "url", "uuid", "windows 0.61.3", - "windows-registry", + "windows-registry 0.5.3", "windows-sys 0.60.2", + "winscard", "zeroize", ] [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "strck" @@ -5189,9 +5402,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.106" +version = "2.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" dependencies = [ "proc-macro2", "quote", @@ -5224,7 +5437,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -5239,6 +5452,12 @@ dependencies = [ "libc", ] +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + [[package]] name = "tap" version = "1.0.1" @@ -5252,10 +5471,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", "rustix 1.1.2", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -5385,9 +5604,9 @@ checksum = "9ab95735ea2c8fd51154d01e39cf13912a78071c2d89abc49a7ef102a7dd725a" [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -5441,29 +5660,26 @@ dependencies = [ [[package]] name = "tokio" -version = "1.47.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "slab", - "socket2 0.6.0", + "socket2 0.6.1", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", @@ -5510,9 +5726,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ "bytes", "futures-core", @@ -5523,9 +5739,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" dependencies = [ "indexmap", "serde_core", @@ -5538,18 +5754,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.23.6" +version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b" +checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" dependencies = [ "indexmap", "toml_datetime", @@ -5559,18 +5775,18 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" dependencies = [ "winnow", ] [[package]] name = "toml_writer" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" [[package]] name = "tower" @@ -5593,7 +5809,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "bytes", "futures-util", "http", @@ -5735,16 +5951,16 @@ dependencies = [ "rand 0.9.2", "rustls", "rustls-pki-types", - "sha1", + "sha1 0.10.6", "thiserror 2.0.17", "utf-8", ] [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unarray" @@ -5754,9 +5970,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-segmentation" @@ -5766,17 +5982,17 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "universal-hash" -version = "0.5.1" +version = "0.6.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +checksum = "a55be643b40a21558f44806b53ee9319595bc7ca6896372e4e08e5d7d83c9cd6" dependencies = [ - "crypto-common", + "crypto-common 0.2.0-rc.4", "subtle", ] @@ -5822,7 +6038,7 @@ version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "js-sys", "serde", "wasm-bindgen", @@ -5911,15 +6127,6 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[package]] -name = "wasi" -version = "0.14.7+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" -dependencies = [ - "wasip2", -] - [[package]] name = "wasip2" version = "1.0.1+wasi-0.2.4" @@ -5937,9 +6144,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" dependencies = [ "cfg-if", "once_cell", @@ -5948,25 +6155,11 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - [[package]] name = "wasm-bindgen-futures" -version = "0.4.54" +version = "0.4.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" +checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" dependencies = [ "cfg-if", "js-sys", @@ -5977,9 +6170,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5987,22 +6180,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.104" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" dependencies = [ "unicode-ident", ] @@ -6027,7 +6220,7 @@ version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "rustix 1.1.2", "wayland-backend", "wayland-scanner", @@ -6039,7 +6232,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cursor-icon", "wayland-backend", ] @@ -6061,7 +6254,7 @@ version = "0.32.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -6073,7 +6266,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6086,7 +6279,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6118,9 +6311,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.81" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" +checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" dependencies = [ "js-sys", "wasm-bindgen", @@ -6138,9 +6331,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" dependencies = [ "rustls-pki-types", ] @@ -6158,9 +6351,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" [[package]] name = "winapi" @@ -6184,7 +6377,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -6369,6 +6562,17 @@ dependencies = [ "windows-strings 0.4.2", ] +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + [[package]] name = "windows-result" version = "0.1.2" @@ -6456,14 +6660,14 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.4", + "windows-targets 0.53.5", ] [[package]] name = "windows-sys" -version = "0.61.1" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link 0.2.1", ] @@ -6516,19 +6720,19 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.4" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d42b7b7f66d2a06854650af09cfdf8713e427a439c97ad65a6375318033ac4b" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ "windows-link 0.2.1", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -6569,9 +6773,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -6593,9 +6797,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -6617,9 +6821,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -6629,9 +6833,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -6653,9 +6857,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -6677,9 +6881,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -6701,9 +6905,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -6725,9 +6929,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winit" @@ -6738,7 +6942,7 @@ dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.9.4", + "bitflags 2.10.0", "block2", "bytemuck", "calloop", @@ -6810,6 +7014,29 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "winscard" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a17695372a560c4f47f2e24e7754cdcc138fd6e62b8b54801d69f19a121a1c" +dependencies = [ + "bitflags 2.10.0", + "crypto-bigint", + "flate2", + "iso7816", + "iso7816-tlv", + "num-derive", + "num-traits", + "picky", + "picky-asn1-x509", + "rand_core 0.9.3", + "rsa", + "sha1 0.11.0-rc.2", + "time", + "tracing", + "uuid", +] + [[package]] name = "wit-bindgen" version = "0.46.0" @@ -6818,9 +7045,9 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "wyz" @@ -6865,12 +7092,12 @@ checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" [[package]] name = "x25519-dalek" -version = "2.0.1" +version = "3.0.0-pre.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +checksum = "3a45998121837fd8c92655d2334aa8f3e5ef0645cdfda5b321b13760c548fd55" dependencies = [ "curve25519-dalek", - "rand_core 0.6.4", + "rand_core 0.9.3", "serde", "zeroize", ] @@ -6881,9 +7108,9 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" dependencies = [ - "const-oid", - "der", - "spki", + "const-oid 0.9.6", + "der 0.7.10", + "spki 0.7.3", "tls_codec", ] @@ -6899,7 +7126,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "dlib", "log", "once_cell", @@ -6945,11 +7172,10 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -6957,9 +7183,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", @@ -7039,9 +7265,9 @@ dependencies = [ [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -7050,9 +7276,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -7061,9 +7287,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", diff --git a/crates/ironrdp-connector/Cargo.toml b/crates/ironrdp-connector/Cargo.toml index 2ff693fb..45c22929 100644 --- a/crates/ironrdp-connector/Cargo.toml +++ b/crates/ironrdp-connector/Cargo.toml @@ -27,13 +27,13 @@ ironrdp-core = { path = "../ironrdp-core", version = "0.1" } # public ironrdp-error = { path = "../ironrdp-error", version = "0.1" } # public ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6", features = ["std"] } # public arbitrary = { version = "1", features = ["derive"], optional = true } # public -sspi = "0.16" # public +sspi = { version = "0.18", features = ["scard"] } url = "2.5" # public rand = { version = "0.9", features = ["std"] } # TODO: dependency injection? tracing = { version = "0.1", features = ["log"] } picky-asn1-der = "0.5" -picky-asn1-x509 = "0.14" -picky = "7.0.0-rc.17" +picky-asn1-x509 = "0.15" +picky = "=7.0.0-rc.20" # FIXME: We are pinning with = because the candidate version number counts as the minor number by Cargo, and will be automatically bumped in the Cargo.lock. [lints] workspace = true diff --git a/crates/ironrdp-connector/src/credssp.rs b/crates/ironrdp-connector/src/credssp.rs index 9750b608..866ac508 100644 --- a/crates/ironrdp-connector/src/credssp.rs +++ b/crates/ironrdp-connector/src/credssp.rs @@ -5,6 +5,7 @@ use picky_asn1_x509::{oids, Certificate, ExtensionView, GeneralName}; use sspi::credssp::{self, ClientState, CredSspClient}; use sspi::generator::{Generator, NetworkRequest}; use sspi::negotiate::ProtocolConfig; +use sspi::Secret; use sspi::Username; use tracing::debug; @@ -123,11 +124,13 @@ impl CredsspSequence { certificate: cert, reader_name: config.reader_name.clone(), card_name: None, - container_name: config.container_name.clone(), + container_name: Some(config.container_name.clone()), csp_name: config.csp_name.clone(), pin: pin.as_bytes().to_vec().into(), - private_key_file_index: None, private_key: Some(key.into()), + scard_type: sspi::SmartCardType::Emulated { + scard_pin: Secret::new(pin.as_bytes().to_vec()), + }, }; sspi::Credentials::SmartCard(Box::new(identity)) } diff --git a/crates/ironrdp-tokio/Cargo.toml b/crates/ironrdp-tokio/Cargo.toml index e129ac44..dee2d0bd 100644 --- a/crates/ironrdp-tokio/Cargo.toml +++ b/crates/ironrdp-tokio/Cargo.toml @@ -27,7 +27,7 @@ ironrdp-async = { path = "../ironrdp-async", version = "0.7" } # public ironrdp-connector = { path = "../ironrdp-connector", version = "0.7", optional = true } tokio = { version = "1", features = ["io-util"] } reqwest = { version = "0.12", default-features = false, features = ["http2", "system-proxy"], optional = true } -sspi = { version = "0.16", features = ["network_client", "dns_resolver"], optional = true } +sspi = { version = "0.18", features = ["network_client", "dns_resolver"], optional = true } url = { version = "2.5", optional = true } [lints] diff --git a/crates/ironrdp/Cargo.toml b/crates/ironrdp/Cargo.toml index cd7b5850..21fc3331 100644 --- a/crates/ironrdp/Cargo.toml +++ b/crates/ironrdp/Cargo.toml @@ -61,7 +61,7 @@ async-trait = "0.1" image = { version = "0.25.6", default-features = false, features = ["png"] } pico-args = "0.5" x509-cert = { version = "0.2", default-features = false, features = ["std"] } -sspi = { version = "0.16", features = ["network_client"] } +sspi = { version = "0.18", features = ["network_client"] } tracing = { version = "0.1", features = ["log"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] } tokio-rustls = "0.26" diff --git a/ffi/Cargo.toml b/ffi/Cargo.toml index 22d5871e..574b77d3 100644 --- a/ffi/Cargo.toml +++ b/ffi/Cargo.toml @@ -19,7 +19,7 @@ ironrdp-cliprdr-native.path = "../crates/ironrdp-cliprdr-native" ironrdp-dvc-pipe-proxy.path = "../crates/ironrdp-dvc-pipe-proxy" ironrdp-core = { path = "../crates/ironrdp-core", features = ["alloc"] } ironrdp-rdcleanpath.path = "../crates/ironrdp-rdcleanpath" -sspi = { version = "0.16", features = ["network_client"] } +sspi = { version = "0.18", features = ["network_client"] } thiserror = "2" tracing = { version = "0.1", features = ["log"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] } From 430f70b43f91eb0a40ecfa3e311cc86d181f88c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Cortier?= <3809077+CBenoit@users.noreply.github.com> Date: Thu, 20 Nov 2025 10:01:39 -0500 Subject: [PATCH 33/51] ci(release): set publish = false in ironrdp-client release-plz config (#1038) --- release-plz.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/release-plz.toml b/release-plz.toml index 79409f23..8a33bf6f 100644 --- a/release-plz.toml +++ b/release-plz.toml @@ -11,6 +11,7 @@ release_commits = "^(feat|docs|fix|build|perf)" [[package]] name = "ironrdp-client" git_release_enable = true +publish = false # TODO: enable publishing when ready. # ironrdp-tls does not compile if no backend is specified. # rustls is the most common backend, so we let cargo publish check with it. From 866a6d867454e9ac836d66c3206279aea07e0800 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 04:13:24 -0500 Subject: [PATCH 34/51] build(deps): bump bytesize from 2.2.0 to 2.3.0 (#1040) --- Cargo.lock | 4 ++-- benches/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7120ad75..0231aa03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -555,9 +555,9 @@ checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "bytesize" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c99fa31e08a43eaa5913ef68d7e01c37a2bdce6ed648168239ad33b7d30a9cd8" +checksum = "00f4369ba008f82b968b1acbe31715ec37bd45236fa0726605a36cc3060ea256" [[package]] name = "calloop" diff --git a/benches/Cargo.toml b/benches/Cargo.toml index deb14ee8..cf9a8c7d 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -17,7 +17,7 @@ qoiz = ["ironrdp/qoiz"] [dependencies] anyhow = "1.0.99" async-trait = "0.1.89" -bytesize = "2.1.0" +bytesize = "2.3.0" ironrdp = { path = "../crates/ironrdp", features = [ "server", "pdu", From 742607240c6ce5a0467687ab0ba478c7f8e66b8b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 04:13:45 -0500 Subject: [PATCH 35/51] build(deps): bump clap from 4.5.52 to 4.5.53 in the patch group across 1 directory (#1039) --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0231aa03..13ec22c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -703,9 +703,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.52" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa8120877db0e5c011242f96806ce3c94e0737ab8108532a76a3300a01db2ab8" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" dependencies = [ "clap_builder", "clap_derive", @@ -713,9 +713,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.52" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02576b399397b659c26064fbc92a75fede9d18ffd5f80ca1cd74ddab167016e1" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" dependencies = [ "anstream", "anstyle", From cca323adab9b8fc886a9e98f8e3d3952fedf6449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Larivi=C3=A8re?= Date: Wed, 26 Nov 2025 15:01:04 -0500 Subject: [PATCH 36/51] ci: migrate iron crates to trusted publishing (#1042) All crates migrated: - ironrdp - ironrdp-acceptor - ironrdp-ainput - ironrdp-async - ironrdp-blocking - ironrdp-cliprdr - ironrdp-cliprdr-format - ironrdp-cliprdr-native - ironrdp-connector - ironrdp-core - ironrdp-displaycontrol - ironrdp-dvc - ironrdp-dvc-pipe-proxy - ironrdp-error - ironrdp-futures - ironrdp-graphics - ironrdp-input - ironrdp-pdu - ironrdp-rdcleanpath - ironrdp-rdpdr - ironrdp-rdpdr-native - ironrdp-rdpsnd - ironrdp-rdpsnd-native - ironrdp-server - ironrdp-session - ironrdp-svc - ironrdp-tls - ironrdp-tokio - iron-remote-desktop --- .github/workflows/release-crates.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-crates.yml b/.github/workflows/release-crates.yml index c03b09bd..96d0a78d 100644 --- a/.github/workflows/release-crates.yml +++ b/.github/workflows/release-crates.yml @@ -66,6 +66,8 @@ jobs: name: Release crates environment: cratesio-publish runs-on: ubuntu-latest + permissions: + id-token: write steps: - name: Checkout repository @@ -73,8 +75,12 @@ jobs: with: fetch-depth: 512 + - name: Authenticate with crates.io + id: auth + uses: rust-lang/crates-io-auth-action@v1 + - name: Run release-plz uses: Devolutions/actions-public/release-plz@v1 with: command: release - registry-token: ${{ secrets.CRATES_IO_TOKEN }} + registry-token: ${{ steps.auth.outputs.token }} From bca6d190a870708468534d224ff225a658767a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Cortier?= <3809077+CBenoit@users.noreply.github.com> Date: Wed, 3 Dec 2025 02:31:12 -0500 Subject: [PATCH 37/51] fix(ironrdp-async)!: use static dispatch for NetworkClient trait (#1043) - Rename `AsyncNetworkClient` to `NetworkClient` - Replace dynamic dispatch (`Option<&mut dyn ...>`) with static dispatch using generics (`&mut N where N: NetworkClient`) - Reorder `connect_finalize` parameters for consistency across crates --- crates/ironrdp-acceptor/src/credssp.rs | 4 +- crates/ironrdp-acceptor/src/lib.rs | 101 ++++++++++---------- crates/ironrdp-async/src/connector.rs | 37 ++++--- crates/ironrdp-async/src/lib.rs | 12 +-- crates/ironrdp-blocking/src/connector.rs | 12 +-- crates/ironrdp-client/src/rdp.rs | 8 +- crates/ironrdp-server/src/server.rs | 2 +- crates/ironrdp-testsuite-extra/tests/mod.rs | 4 +- crates/ironrdp-tokio/src/reqwest.rs | 13 +-- crates/ironrdp-web/src/network_client.rs | 68 ++++++------- crates/ironrdp-web/src/session.rs | 4 +- crates/ironrdp/examples/screenshot.rs | 4 +- 12 files changed, 123 insertions(+), 146 deletions(-) diff --git a/crates/ironrdp-acceptor/src/credssp.rs b/crates/ironrdp-acceptor/src/credssp.rs index f8840780..cf50ab56 100644 --- a/crates/ironrdp-acceptor/src/credssp.rs +++ b/crates/ironrdp-acceptor/src/credssp.rs @@ -1,4 +1,4 @@ -use ironrdp_async::AsyncNetworkClient; +use ironrdp_async::NetworkClient; use ironrdp_connector::sspi::credssp::{ CredSspServer, CredentialsProxy, ServerError, ServerMode, ServerState, TsRequest, }; @@ -71,7 +71,7 @@ impl CredentialsProxy for CredentialsProxyImpl<'_> { pub(crate) async fn resolve_generator( generator: &mut CredsspProcessGenerator<'_>, - network_client: &mut dyn AsyncNetworkClient, + network_client: &mut impl NetworkClient, ) -> Result { let mut state = generator.start(); diff --git a/crates/ironrdp-acceptor/src/lib.rs b/crates/ironrdp-acceptor/src/lib.rs index aea782a4..32bc2710 100644 --- a/crates/ironrdp-acceptor/src/lib.rs +++ b/crates/ironrdp-acceptor/src/lib.rs @@ -1,7 +1,7 @@ #![cfg_attr(doc, doc = include_str!("../README.md"))] #![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")] -use ironrdp_async::{single_sequence_step, AsyncNetworkClient, Framed, FramedRead, FramedWrite, StreamWrapper}; +use ironrdp_async::{single_sequence_step, Framed, FramedRead, FramedWrite, NetworkClient, StreamWrapper}; use ironrdp_connector::sspi::credssp::EarlyUserAuthResult; use ironrdp_connector::sspi::{AuthIdentity, KerberosServerConfig, Username}; use ironrdp_connector::{custom_err, general_err, ConnectorResult, ServerName}; @@ -51,16 +51,17 @@ where } } -pub async fn accept_credssp( +pub async fn accept_credssp( framed: &mut Framed, acceptor: &mut Acceptor, + network_client: &mut N, client_computer_name: ServerName, public_key: Vec, kerberos_config: Option, - network_client: Option<&mut dyn AsyncNetworkClient>, ) -> ConnectorResult<()> where S: FramedRead + FramedWrite, + N: NetworkClient, { let mut buf = WriteBuf::new(); @@ -68,11 +69,11 @@ where perform_credssp_step( framed, acceptor, + network_client, &mut buf, client_computer_name, public_key, kerberos_config, - network_client, ) .await } else { @@ -98,34 +99,73 @@ where } #[instrument(level = "trace", skip_all, ret)] -async fn perform_credssp_step( +async fn perform_credssp_step( framed: &mut Framed, acceptor: &mut Acceptor, + network_client: &mut N, buf: &mut WriteBuf, client_computer_name: ServerName, public_key: Vec, kerberos_config: Option, - network_client: Option<&mut dyn AsyncNetworkClient>, ) -> ConnectorResult<()> where S: FramedRead + FramedWrite, + N: NetworkClient, { assert!(acceptor.should_perform_credssp()); let AcceptorState::Credssp { protocol, .. } = acceptor.state else { unreachable!() }; - async fn credssp_loop( + let result = credssp_loop( + framed, + acceptor, + network_client, + buf, + client_computer_name, + public_key, + kerberos_config, + ) + .await; + + if protocol.intersects(nego::SecurityProtocol::HYBRID_EX) { + trace!(?result, "HYBRID_EX"); + + let result = if result.is_ok() { + EarlyUserAuthResult::Success + } else { + EarlyUserAuthResult::AccessDenied + }; + + buf.clear(); + result + .to_buffer(&mut *buf) + .map_err(|e| ironrdp_connector::custom_err!("to_buffer", e))?; + let response = &buf[..result.buffer_len()]; + framed + .write_all(response) + .await + .map_err(|e| ironrdp_connector::custom_err!("write all", e))?; + } + + result?; + + acceptor.mark_credssp_as_done(); + + return Ok(()); + + async fn credssp_loop( framed: &mut Framed, acceptor: &mut Acceptor, + network_client: &mut N, buf: &mut WriteBuf, client_computer_name: ServerName, public_key: Vec, kerberos_config: Option, - mut network_client: Option<&mut dyn AsyncNetworkClient>, ) -> ConnectorResult<()> where S: FramedRead + FramedWrite, + N: NetworkClient, { let creds = acceptor .creds @@ -164,12 +204,7 @@ where let result = { let mut generator = sequence.process_ts_request(ts_request); - - if let Some(network_client_ref) = network_client.as_deref_mut() { - resolve_generator(&mut generator, network_client_ref).await - } else { - generator.resolve_to_result() - } + resolve_generator(&mut generator, network_client).await }; // drop generator buf.clear(); @@ -184,43 +219,7 @@ where .map_err(|e| ironrdp_connector::custom_err!("write all", e))?; } } + Ok(()) } - - let result = credssp_loop( - framed, - acceptor, - buf, - client_computer_name, - public_key, - kerberos_config, - network_client, - ) - .await; - - if protocol.intersects(nego::SecurityProtocol::HYBRID_EX) { - trace!(?result, "HYBRID_EX"); - - let result = if result.is_ok() { - EarlyUserAuthResult::Success - } else { - EarlyUserAuthResult::AccessDenied - }; - - buf.clear(); - result - .to_buffer(&mut *buf) - .map_err(|e| ironrdp_connector::custom_err!("to_buffer", e))?; - let response = &buf[..result.buffer_len()]; - framed - .write_all(response) - .await - .map_err(|e| ironrdp_connector::custom_err!("write all", e))?; - } - - result?; - - acceptor.mark_credssp_as_done(); - - Ok(()) } diff --git a/crates/ironrdp-async/src/connector.rs b/crates/ironrdp-async/src/connector.rs index 823cfe10..04f3d5a7 100644 --- a/crates/ironrdp-async/src/connector.rs +++ b/crates/ironrdp-async/src/connector.rs @@ -2,14 +2,14 @@ use ironrdp_connector::credssp::{CredsspProcessGenerator, CredsspSequence, Kerbe use ironrdp_connector::sspi::credssp::ClientState; use ironrdp_connector::sspi::generator::GeneratorState; use ironrdp_connector::{ - custom_err, general_err, ClientConnector, ClientConnectorState, ConnectionResult, ConnectorError, ConnectorResult, - ServerName, State as _, + general_err, ClientConnector, ClientConnectorState, ConnectionResult, ConnectorError, ConnectorResult, ServerName, + State as _, }; use ironrdp_core::WriteBuf; use tracing::{debug, info, instrument, trace}; use crate::framed::{Framed, FramedRead, FramedWrite}; -use crate::{single_sequence_step, AsyncNetworkClient}; +use crate::{single_sequence_step, NetworkClient}; #[non_exhaustive] pub struct ShouldUpgrade; @@ -49,28 +49,29 @@ pub fn mark_as_upgraded(_: ShouldUpgrade, connector: &mut ClientConnector) -> Up } #[instrument(skip_all)] -pub async fn connect_finalize( +pub async fn connect_finalize( _: Upgraded, - framed: &mut Framed, mut connector: ClientConnector, + framed: &mut Framed, + network_client: &mut N, server_name: ServerName, server_public_key: Vec, - network_client: Option<&mut dyn AsyncNetworkClient>, kerberos_config: Option, ) -> ConnectorResult where S: FramedRead + FramedWrite, + N: NetworkClient, { let mut buf = WriteBuf::new(); if connector.should_perform_credssp() { perform_credssp_step( - framed, &mut connector, + framed, + network_client, &mut buf, server_name, server_public_key, - network_client, kerberos_config, ) .await?; @@ -91,7 +92,7 @@ where async fn resolve_generator( generator: &mut CredsspProcessGenerator<'_>, - network_client: &mut dyn AsyncNetworkClient, + network_client: &mut impl NetworkClient, ) -> ConnectorResult { let mut state = generator.start(); @@ -110,17 +111,18 @@ async fn resolve_generator( } #[instrument(level = "trace", skip_all)] -async fn perform_credssp_step( - framed: &mut Framed, +async fn perform_credssp_step( connector: &mut ClientConnector, + framed: &mut Framed, + network_client: &mut N, buf: &mut WriteBuf, server_name: ServerName, server_public_key: Vec, - mut network_client: Option<&mut dyn AsyncNetworkClient>, kerberos_config: Option, ) -> ConnectorResult<()> where S: FramedRead + FramedWrite, + N: NetworkClient, { assert!(connector.should_perform_credssp()); @@ -141,15 +143,8 @@ where loop { let client_state = { let mut generator = sequence.process_ts_request(ts_request); - - if let Some(network_client_ref) = network_client.as_deref_mut() { - trace!("resolving network"); - resolve_generator(&mut generator, network_client_ref).await? - } else { - generator - .resolve_to_result() - .map_err(|e| custom_err!("resolve without network client", e))? - } + trace!("resolving network"); + resolve_generator(&mut generator, network_client).await? }; // drop generator buf.clear(); diff --git a/crates/ironrdp-async/src/lib.rs b/crates/ironrdp-async/src/lib.rs index b3677535..847200c0 100644 --- a/crates/ironrdp-async/src/lib.rs +++ b/crates/ironrdp-async/src/lib.rs @@ -1,15 +1,14 @@ #![cfg_attr(doc, doc = include_str!("../README.md"))] #![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")] +use core::future::Future; + pub use bytes; mod connector; mod framed; mod session; -use core::future::Future; -use core::pin::Pin; - use ironrdp_connector::sspi::generator::NetworkRequest; use ironrdp_connector::ConnectorResult; @@ -17,9 +16,6 @@ pub use self::connector::*; pub use self::framed::*; // pub use self::session::*; -pub trait AsyncNetworkClient { - fn send<'a>( - &'a mut self, - network_request: &'a NetworkRequest, - ) -> Pin>> + 'a>>; +pub trait NetworkClient { + fn send(&mut self, network_request: &NetworkRequest) -> impl Future>>; } diff --git a/crates/ironrdp-blocking/src/connector.rs b/crates/ironrdp-blocking/src/connector.rs index d601dd98..b50805ae 100644 --- a/crates/ironrdp-blocking/src/connector.rs +++ b/crates/ironrdp-blocking/src/connector.rs @@ -53,11 +53,11 @@ pub fn mark_as_upgraded(_: ShouldUpgrade, connector: &mut ClientConnector) -> Up #[instrument(skip_all)] pub fn connect_finalize( _: Upgraded, - framed: &mut Framed, mut connector: ClientConnector, + framed: &mut Framed, + network_client: &mut impl NetworkClient, server_name: ServerName, server_public_key: Vec, - network_client: &mut impl NetworkClient, kerberos_config: Option, ) -> ConnectorResult where @@ -69,12 +69,12 @@ where if connector.should_perform_credssp() { perform_credssp_step( - framed, &mut connector, + framed, + network_client, &mut buf, server_name, server_public_key, - network_client, kerberos_config, )?; } @@ -118,12 +118,12 @@ fn resolve_generator( #[instrument(level = "trace", skip_all)] fn perform_credssp_step( - framed: &mut Framed, connector: &mut ClientConnector, + framed: &mut Framed, + network_client: &mut impl NetworkClient, buf: &mut WriteBuf, server_name: ServerName, server_public_key: Vec, - network_client: &mut impl NetworkClient, kerberos_config: Option, ) -> ConnectorResult<()> where diff --git a/crates/ironrdp-client/src/rdp.rs b/crates/ironrdp-client/src/rdp.rs index c1bd9aef..23f5c0ea 100644 --- a/crates/ironrdp-client/src/rdp.rs +++ b/crates/ironrdp-client/src/rdp.rs @@ -240,11 +240,11 @@ async fn connect( let connection_result = ironrdp_tokio::connect_finalize( upgraded, - &mut upgraded_framed, connector, + &mut upgraded_framed, + &mut ReqwestNetworkClient::new(), (&config.destination).into(), server_public_key, - Some(&mut ReqwestNetworkClient::new()), None, ) .await?; @@ -326,11 +326,11 @@ async fn connect_ws( let connection_result = ironrdp_tokio::connect_finalize( upgraded, - &mut framed, connector, + &mut framed, + &mut ReqwestNetworkClient::new(), (&config.destination).into(), server_public_key, - Some(&mut ReqwestNetworkClient::new()), None, ) .await?; diff --git a/crates/ironrdp-server/src/server.rs b/crates/ironrdp-server/src/server.rs index def121a5..9fe234d8 100644 --- a/crates/ironrdp-server/src/server.rs +++ b/crates/ironrdp-server/src/server.rs @@ -349,10 +349,10 @@ impl RdpServer { ironrdp_acceptor::accept_credssp( &mut framed, &mut acceptor, + &mut ironrdp_tokio::reqwest::ReqwestNetworkClient::new(), client_name.into(), pub_key.clone(), None, - None, ) .await?; } diff --git a/crates/ironrdp-testsuite-extra/tests/mod.rs b/crates/ironrdp-testsuite-extra/tests/mod.rs index 8f5db6d6..e5bade18 100644 --- a/crates/ironrdp-testsuite-extra/tests/mod.rs +++ b/crates/ironrdp-testsuite-extra/tests/mod.rs @@ -212,12 +212,12 @@ where let mut upgraded_framed = ironrdp_tokio::TokioFramed::new(upgraded_stream); let connection_result = ironrdp_async::connect_finalize( upgraded, - &mut upgraded_framed, connector, + &mut upgraded_framed, + &mut ironrdp_tokio::reqwest::ReqwestNetworkClient::new(), "localhost".into(), server_public_key, None, - None, ) .await .expect("finalize connection"); diff --git a/crates/ironrdp-tokio/src/reqwest.rs b/crates/ironrdp-tokio/src/reqwest.rs index b196c2aa..deeee192 100644 --- a/crates/ironrdp-tokio/src/reqwest.rs +++ b/crates/ironrdp-tokio/src/reqwest.rs @@ -1,6 +1,4 @@ -use core::future::Future; use core::net::{IpAddr, Ipv4Addr}; -use core::pin::Pin; use ironrdp_connector::{custom_err, general_err, ConnectorResult}; use reqwest::Client; @@ -9,18 +7,15 @@ use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _}; use tokio::net::{TcpStream, UdpSocket}; use url::Url; -use crate::AsyncNetworkClient; +use crate::NetworkClient; pub struct ReqwestNetworkClient { client: Option, } -impl AsyncNetworkClient for ReqwestNetworkClient { - fn send<'a>( - &'a mut self, - network_request: &'a sspi::generator::NetworkRequest, - ) -> Pin>> + 'a>> { - Box::pin(ReqwestNetworkClient::send_request(self, network_request)) +impl NetworkClient for ReqwestNetworkClient { + async fn send(&mut self, network_request: &sspi::generator::NetworkRequest) -> ConnectorResult> { + ReqwestNetworkClient::send_request(self, network_request).await } } diff --git a/crates/ironrdp-web/src/network_client.rs b/crates/ironrdp-web/src/network_client.rs index f22a3b03..0e32efe8 100644 --- a/crates/ironrdp-web/src/network_client.rs +++ b/crates/ironrdp-web/src/network_client.rs @@ -1,53 +1,45 @@ -use core::pin::Pin; - -use futures_util::Future; use ironrdp::connector::sspi::generator::NetworkRequest; use ironrdp::connector::sspi::network_client::NetworkProtocol; use ironrdp::connector::{custom_err, reason_err, ConnectorResult}; -use ironrdp_futures::AsyncNetworkClient; +use ironrdp_futures::NetworkClient; use tracing::debug; #[derive(Debug)] pub(crate) struct WasmNetworkClient; -impl AsyncNetworkClient for WasmNetworkClient { - fn send<'a>( - &'a mut self, - network_request: &'a NetworkRequest, - ) -> Pin>> + 'a>> { - Box::pin(async move { - debug!(?network_request.protocol, ?network_request.url); +impl NetworkClient for WasmNetworkClient { + async fn send(&mut self, network_request: &NetworkRequest) -> ConnectorResult> { + debug!(?network_request.protocol, ?network_request.url); - match &network_request.protocol { - NetworkProtocol::Http | NetworkProtocol::Https => { - let body = js_sys::Uint8Array::from(network_request.data.as_slice()); + match &network_request.protocol { + NetworkProtocol::Http | NetworkProtocol::Https => { + let body = js_sys::Uint8Array::from(network_request.data.as_slice()); - let response = gloo_net::http::Request::post(network_request.url.as_str()) - .header("keep-alive", "true") - .body(body) - .map_err(|e| custom_err!("failed to send KDC request", e))? - .send() - .await - .map_err(|e| custom_err!("failed to send KDC request", e))?; + let response = gloo_net::http::Request::post(network_request.url.as_str()) + .header("keep-alive", "true") + .body(body) + .map_err(|e| custom_err!("failed to send KDC request", e))? + .send() + .await + .map_err(|e| custom_err!("failed to send KDC request", e))?; - if !response.ok() { - return Err(reason_err!( - "KdcProxy", - "HTTP status error ({} {})", - response.status(), - response.status_text(), - )); - } - - let body = response - .binary() - .await - .map_err(|e| custom_err!("failed to retrieve HTTP response", e))?; - - Ok(body) + if !response.ok() { + return Err(reason_err!( + "KdcProxy", + "HTTP status error ({} {})", + response.status(), + response.status_text(), + )); } - unsupported => Err(reason_err!("CredSSP", "unsupported protocol: {unsupported:?}")), + + let body = response + .binary() + .await + .map_err(|e| custom_err!("failed to retrieve HTTP response", e))?; + + Ok(body) } - }) + unsupported => Err(reason_err!("CredSSP", "unsupported protocol: {unsupported:?}")), + } } } diff --git a/crates/ironrdp-web/src/session.rs b/crates/ironrdp-web/src/session.rs index 0c7378de..6d0e018a 100644 --- a/crates/ironrdp-web/src/session.rs +++ b/crates/ironrdp-web/src/session.rs @@ -979,11 +979,11 @@ async fn connect( let connection_result = ironrdp_futures::connect_finalize( upgraded, - &mut framed, connector, + &mut framed, + &mut WasmNetworkClient, (&destination).into(), server_public_key, - Some(&mut WasmNetworkClient), url::Url::parse(kdc_proxy_url.unwrap_or_default().as_str()) // if kdc_proxy_url does not exit, give url parser a empty string, it will fail anyway and map to a None .ok() .map(|url| KerberosConfig { diff --git a/crates/ironrdp/examples/screenshot.rs b/crates/ironrdp/examples/screenshot.rs index 94144ce6..64ff33ac 100644 --- a/crates/ironrdp/examples/screenshot.rs +++ b/crates/ironrdp/examples/screenshot.rs @@ -258,11 +258,11 @@ fn connect( let mut network_client = ReqwestNetworkClient; let connection_result = ironrdp_blocking::connect_finalize( upgraded, - &mut upgraded_framed, connector, + &mut upgraded_framed, + &mut network_client, server_name.into(), server_public_key, - &mut network_client, None, ) .context("finalize connection")?; From b303ae3a903d27e53d11fa223989e29a71d598b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Dec 2025 02:36:06 -0500 Subject: [PATCH 38/51] build(deps): bump criterion from 0.7.0 to 0.8.0 (#1045) Bumps [criterion](https://github.com/criterion-rs/criterion.rs) from 0.7.0 to 0.8.0.
Release notes

Sourced from criterion's releases.

criterion-plot-v0.8.0

No release notes provided.

criterion-v0.8.0

BREAKING

  • Drop async-std support

Changed

  • Bump MSRV to 1.86, stable to 1.91.1

Added

  • Add ability to plot throughput on summary page.
  • Add support for reporting throughput in elements and bytes - Throughput::ElementsAndBytes allows the text summary to report throughput in both units simultaneously.
  • Add alloca-based memory layout randomisation to mitigate memory effects on measurements.
  • Add doc comment to benchmark runner in criterion_group macro (removes linter warnings)

Fixed

  • Fix plotting NaN bug

Other

  • Remove Master API Docs links temporarily while we restore the docs publishing.
Changelog

Sourced from criterion's changelog.

0.8.0 - 2025-11-29

BREAKING

  • Drop async-std support

Changed

  • Bump MSRV to 1.86, stable to 1.91.1

Added

  • Add ability to plot throughput on summary page.
  • Add support for reporting throughput in elements and bytes - Throughput::ElementsAndBytes allows the text summary to report throughput in both units simultaneously.
  • Add alloca-based memory layout randomisation to mitigate memory effects on measurements.
  • Add doc comment to benchmark runner in criterion_group macro (removes linter warnings)

Fixed

  • Fix plotting NaN bug

Other

  • Remove Master API Docs links temporarily while we restore the docs publishing.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=criterion&package-manager=cargo&previous-version=0.7.0&new-version=0.8.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 29 +++++++++++++++++++++++++---- crates/ironrdp-bench/Cargo.toml | 2 +- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 13ec22c7..6b860544 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,6 +91,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + [[package]] name = "alsa" version = "0.9.1" @@ -940,10 +949,11 @@ dependencies = [ [[package]] name = "criterion" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1c047a62b0cc3e145fa84415a3191f628e980b194c2755aa12300a4e6cbd928" +checksum = "a0dfe5e9e71bdcf4e4954f7d14da74d1cdb92a3a07686452d1509652684b1aab" dependencies = [ + "alloca", "anes", "cast", "ciborium", @@ -952,6 +962,7 @@ dependencies = [ "itertools", "num-traits", "oorandom", + "page_size", "plotters", "rayon", "regex", @@ -963,9 +974,9 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1bcc0dc7dfae599d84ad0b1a55f80cde8af3725da8313b528da95ef783e338" +checksum = "5de36c2bee19fba779808f92bf5d9b0fa5a40095c277aba10c458a12b35d21d6" dependencies = [ "cast", "itertools", @@ -3861,6 +3872,16 @@ dependencies = [ "sha2", ] +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "parking_lot" version = "0.12.5" diff --git a/crates/ironrdp-bench/Cargo.toml b/crates/ironrdp-bench/Cargo.toml index 3ab6a658..ea35d2ce 100644 --- a/crates/ironrdp-bench/Cargo.toml +++ b/crates/ironrdp-bench/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true publish = false [dev-dependencies] -criterion = "0.7" +criterion = "0.8" ironrdp-graphics.path = "../ironrdp-graphics" ironrdp-pdu.path = "../ironrdp-pdu" ironrdp-server = { path = "../ironrdp-server", features = ["__bench"] } From cf978321d3360e2f84d6405cc1ddd5782bbc5d99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Dec 2025 07:52:21 +0000 Subject: [PATCH 39/51] build(deps): bump the patch group across 1 directory with 6 updates (#1044) --- Cargo.lock | 52 +++++++++++++++++++++++----------------------- benches/Cargo.toml | 2 +- fuzz/Cargo.lock | 16 +++++++------- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6b860544..3312e0dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -564,9 +564,9 @@ checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "bytesize" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f4369ba008f82b968b1acbe31715ec37bd45236fa0726605a36cc3060ea256" +checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3" [[package]] name = "calloop" @@ -3067,9 +3067,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -5856,9 +5856,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" dependencies = [ "log", "pin-project-lite", @@ -5868,9 +5868,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", @@ -5879,9 +5879,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" dependencies = [ "once_cell", "valuable", @@ -5900,9 +5900,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "matchers", "nu-ansi-term", @@ -6165,9 +6165,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -6178,9 +6178,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.55" +version = "0.4.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" dependencies = [ "cfg-if", "js-sys", @@ -6191,9 +6191,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6201,9 +6201,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ "bumpalo", "proc-macro2", @@ -6214,9 +6214,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] @@ -6332,9 +6332,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.82" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", @@ -7216,9 +7216,9 @@ dependencies = [ [[package]] name = "yuv" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30078f3e5790a2127f89c57c4ccb46d060205bf2c4c267f77cd08fb5c02c6d79" +checksum = "28f1bad143caadcfcaec93039dc9c40a30fc86f23d9e7cc03764a39fe51d9d43" dependencies = [ "num-traits", ] diff --git a/benches/Cargo.toml b/benches/Cargo.toml index cf9a8c7d..19a87c6f 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -17,7 +17,7 @@ qoiz = ["ironrdp/qoiz"] [dependencies] anyhow = "1.0.99" async-trait = "0.1.89" -bytesize = "2.3.0" +bytesize = "2.3" ironrdp = { path = "../crates/ironrdp", features = [ "server", "pdu", diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index c91b71f6..2a1fd15c 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -727,9 +727,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" dependencies = [ "log", "pin-project-lite", @@ -739,9 +739,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", @@ -750,9 +750,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" dependencies = [ "once_cell", ] @@ -813,9 +813,9 @@ dependencies = [ [[package]] name = "yuv" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30078f3e5790a2127f89c57c4ccb46d060205bf2c4c267f77cd08fb5c02c6d79" +checksum = "28f1bad143caadcfcaec93039dc9c40a30fc86f23d9e7cc03764a39fe51d9d43" dependencies = [ "num-traits", ] From 924330159a41d208db29ad457fe863bdedce912c Mon Sep 17 00:00:00 2001 From: Alex Yusiuk <55661041+RRRadicalEdward@users.noreply.github.com> Date: Wed, 3 Dec 2025 15:07:33 +0200 Subject: [PATCH 40/51] chore: enable `large_futures` clippy lint (#1046) > It checks for the size of a Future created by async fn or async {}. The maximum byte size a `Future` can have, before it triggers the clippy::large_futures lint is by default 16384, but we can adjust it. --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 135c56f4..203c1be7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -156,6 +156,7 @@ or_fun_call = "warn" rc_buffer = "warn" string_lit_chars_any = "warn" unnecessary_box_returns = "warn" +large_futures = "warn" # == Extra-pedantic clippy == # allow_attributes = "warn" From a2af587e60e869f0235703e21772d1fc6a7dadcd Mon Sep 17 00:00:00 2001 From: Richard Markiewicz Date: Thu, 4 Dec 2025 02:38:05 -0500 Subject: [PATCH 41/51] fix(cliprdr): prevent window class registration error on multiple sessions (#1047) When starting a second clipboard session, `RegisterClassA` would fail with `ERROR_CLASS_ALREADY_EXISTS` because window classes are global to the process. Now checks if the class is already registered before attempting registration, allowing multiple WinClipboard instances to coexist. --- .../ironrdp-cliprdr-native/src/windows/mod.rs | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/crates/ironrdp-cliprdr-native/src/windows/mod.rs b/crates/ironrdp-cliprdr-native/src/windows/mod.rs index 48bf2dbc..87574ca5 100644 --- a/crates/ironrdp-cliprdr-native/src/windows/mod.rs +++ b/crates/ironrdp-cliprdr-native/src/windows/mod.rs @@ -19,7 +19,8 @@ use windows::Win32::System::DataExchange::{AddClipboardFormatListener, RemoveCli use windows::Win32::System::LibraryLoader::GetModuleHandleA; use windows::Win32::UI::Shell::{RemoveWindowSubclass, SetWindowSubclass}; use windows::Win32::UI::WindowsAndMessaging::{ - CreateWindowExA, DefWindowProcA, RegisterClassA, CW_USEDEFAULT, WINDOW_EX_STYLE, WM_USER, WNDCLASSA, WS_POPUP, + CreateWindowExA, DefWindowProcA, GetClassInfoA, RegisterClassA, CW_USEDEFAULT, WINDOW_EX_STYLE, WM_USER, WNDCLASSA, + WS_POPUP, }; use self::clipboard_impl::{clipboard_subproc, WinClipboardImpl}; @@ -152,17 +153,25 @@ impl WinClipboard { // SAFETY: low-level WinAPI call let instance = unsafe { GetModuleHandleA(None)? }; let window_class = s!("IronRDPClipboardMonitor"); - let wc = WNDCLASSA { - hInstance: instance.into(), - lpszClassName: window_class, - lpfnWndProc: Some(wndproc), - ..Default::default() - }; - // SAFETY: low-level WinAPI call - let atom = unsafe { RegisterClassA(&wc) }; - if atom == 0 { - return Err(WinCliprdrError::from(Error::from_thread())); + let mut existing_wc = WNDCLASSA::default(); + // SAFETY: `instance` is a valid module handle, `window_class` is a valid null-terminated string, + // and `existing_wc` is a valid mutable reference to a WNDCLASSA structure. + let class_exists = unsafe { GetClassInfoA(Some(instance.into()), window_class, &mut existing_wc).is_ok() }; + + if !class_exists { + let wc = WNDCLASSA { + hInstance: instance.into(), + lpszClassName: window_class, + lpfnWndProc: Some(wndproc), + ..Default::default() + }; + + // SAFETY: low-level WinAPI call + let atom = unsafe { RegisterClassA(&wc) }; + if atom == 0 { + return Err(WinCliprdrError::from(Error::from_thread())); + } } // SAFETY: low-level WinAPI call From da5db5bf118bd2920b70ac9ce790a5bd17a1e2df Mon Sep 17 00:00:00 2001 From: Richard Markiewicz Date: Thu, 4 Dec 2025 10:11:34 -0500 Subject: [PATCH 42/51] chore(release): prepare for Devolutions.IronRdp v2025.12.4.0 (#1048) We still don't have a means to set the nuget version directly in the workflow. I thought of adding it, but on closer inspection we have logic to handle package and product version differently (and the same is true of sspi-rs etc) and probably needs a closer look. It can be troublesome to deploy a newer nuget package that doesn't increment the assembly versions (for example - and it shouldn't be an issue for Devolutions, but maybe for other consumers - Windows Installers generally might not overwrite a DLL if the version number is not newer than what is already installed. For now, I just bump the package version manually. --- ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.csproj b/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.csproj index 5ae81afe..38fc2e65 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.csproj +++ b/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.csproj @@ -4,7 +4,7 @@ Devolutions Bindings to Rust IronRDP native library latest - 2025.9.24.0 + 2025.12.4.0 enable enable true From 632ad86f6733712ade808d0b5703f913ec08002e Mon Sep 17 00:00:00 2001 From: Richard Markiewicz Date: Thu, 4 Dec 2025 11:45:49 -0500 Subject: [PATCH 43/51] chore: bump iOS nuget package to net9.0 (#1049) The `net8.0-ios` workload is out of support and won't build out-of-the-box anymore. Bump the target framework to `net9.0-ios`. --- .github/workflows/nuget-publish.yml | 2 +- .../Devolutions.IronRdp/Devolutions.IronRdp.Build.iOS.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nuget-publish.yml b/.github/workflows/nuget-publish.yml index 2b9d7b0d..eb6350b0 100644 --- a/.github/workflows/nuget-publish.yml +++ b/.github/workflows/nuget-publish.yml @@ -337,7 +337,7 @@ jobs: run: | # net8.0 target packaged as Devolutions.IronRdp dotnet build .\ffi\dotnet\Devolutions.IronRdp\Devolutions.IronRdp.csproj -c Release - # net8.0-ios target packaged as Devolutions.IronRdp.iOS + # net9.0-ios target packaged as Devolutions.IronRdp.iOS dotnet build .\ffi\dotnet\Devolutions.IronRdp\Devolutions.IronRdp.csproj -c Release /p:PackageId=Devolutions.IronRdp.iOS shell: pwsh diff --git a/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.Build.iOS.props b/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.Build.iOS.props index af8d97cc..b692fdbb 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.Build.iOS.props +++ b/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.Build.iOS.props @@ -1,6 +1,6 @@ - net8.0-ios + net9.0-ios 12.1 From 7123150b63879c73bc9ea0d1af96f1eca670ac4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Cortier?= <3809077+CBenoit@users.noreply.github.com> Date: Fri, 5 Dec 2025 10:43:28 -0500 Subject: [PATCH 44/51] fix(ffi): preserve full error context across FFI boundary (#1050) Replaced generic From implementation that used to_string() with specific implementations for each error type to ensure source error chains are preserved. IronRDP errors now use .report() for full context, while standard library errors are converted to anyhow::Error for proper alternate formatting with {:#}. --- ffi/src/error.rs | 270 +++++++++++++++++++++++++++-------------------- 1 file changed, 157 insertions(+), 113 deletions(-) diff --git a/ffi/src/error.rs b/ffi/src/error.rs index ceaa4e4e..11ae4e2b 100644 --- a/ffi/src/error.rs +++ b/ffi/src/error.rs @@ -10,102 +10,6 @@ use ironrdp_rdcleanpath::der; use self::ffi::IronRdpErrorKind; -impl From for IronRdpErrorKind { - fn from(val: ConnectorError) -> Self { - match val.kind() { - ironrdp::connector::ConnectorErrorKind::Encode(_) => IronRdpErrorKind::EncodeError, - ironrdp::connector::ConnectorErrorKind::Decode(_) => IronRdpErrorKind::DecodeError, - ironrdp::connector::ConnectorErrorKind::Credssp(_) => IronRdpErrorKind::CredsspError, - ironrdp::connector::ConnectorErrorKind::AccessDenied => IronRdpErrorKind::AccessDenied, - _ => IronRdpErrorKind::Generic, - } - } -} - -impl From<&str> for IronRdpErrorKind { - fn from(_val: &str) -> Self { - IronRdpErrorKind::Generic - } -} - -impl From for IronRdpErrorKind { - fn from(_val: sspi::Error) -> Self { - IronRdpErrorKind::CredsspError - } -} - -impl From for IronRdpErrorKind { - fn from(_val: ironrdp::pdu::PduError) -> Self { - IronRdpErrorKind::PduError - } -} - -impl From for IronRdpErrorKind { - fn from(_val: ironrdp::core::EncodeError) -> Self { - IronRdpErrorKind::EncodeError - } -} - -impl From for IronRdpErrorKind { - fn from(_val: ironrdp::core::DecodeError) -> Self { - IronRdpErrorKind::DecodeError - } -} - -impl From for IronRdpErrorKind { - fn from(_: std::io::Error) -> Self { - IronRdpErrorKind::IO - } -} - -impl From for IronRdpErrorKind { - fn from(_val: core::fmt::Error) -> Self { - IronRdpErrorKind::Generic - } -} - -impl From for IronRdpErrorKind { - fn from(value: SessionError) -> Self { - match value.kind() { - ironrdp::session::SessionErrorKind::Pdu(_) => IronRdpErrorKind::PduError, - ironrdp::session::SessionErrorKind::Encode(_) => IronRdpErrorKind::EncodeError, - ironrdp::session::SessionErrorKind::Decode(_) => IronRdpErrorKind::DecodeError, - _ => IronRdpErrorKind::Generic, - } - } -} - -impl From<&dyn ClipboardError> for IronRdpErrorKind { - fn from(_val: &dyn ClipboardError) -> Self { - IronRdpErrorKind::Clipboard - } -} - -#[cfg(target_os = "windows")] -impl From for IronRdpErrorKind { - fn from(_val: WinCliprdrError) -> Self { - IronRdpErrorKind::Clipboard - } -} - -impl From for IronRdpErrorKind { - fn from(_val: WrongOSError) -> Self { - IronRdpErrorKind::WrongOS - } -} - -impl From for IronRdpErrorKind { - fn from(_val: der::Error) -> Self { - IronRdpErrorKind::DecodeError - } -} - -impl From for IronRdpErrorKind { - fn from(_val: ironrdp_rdcleanpath::MissingRDCleanPathField) -> Self { - IronRdpErrorKind::Generic - } -} - pub struct GenericError(pub anyhow::Error); impl Display for GenericError { @@ -114,28 +18,168 @@ impl Display for GenericError { } } -impl From for IronRdpErrorKind { - fn from(_val: GenericError) -> Self { - IronRdpErrorKind::Generic - } -} - -impl From for Box -where - T: Into + ToString, -{ - fn from(value: T) -> Self { - let repr = value.to_string(); - let kind = value.into(); - Box::new(ffi::IronRdpError(IronRdpErrorInner { repr, kind })) - } -} - struct IronRdpErrorInner { repr: String, kind: IronRdpErrorKind, } +// Helper function to create an IronRdpError +fn make_ffi_error(repr: String, kind: IronRdpErrorKind) -> Box { + Box::new(ffi::IronRdpError(IronRdpErrorInner { repr, kind })) +} + +// Direct conversion from IronRdpErrorKind (for cases with no underlying error) +impl From for Box { + fn from(kind: IronRdpErrorKind) -> Self { + make_ffi_error(kind.to_string(), kind) + } +} + +// IronRDP errors - use .report() to include full error chain with sources +impl From for Box { + fn from(value: ConnectorError) -> Self { + let kind = match value.kind() { + ironrdp::connector::ConnectorErrorKind::Encode(_) => IronRdpErrorKind::EncodeError, + ironrdp::connector::ConnectorErrorKind::Decode(_) => IronRdpErrorKind::DecodeError, + ironrdp::connector::ConnectorErrorKind::Credssp(_) => IronRdpErrorKind::CredsspError, + ironrdp::connector::ConnectorErrorKind::AccessDenied => IronRdpErrorKind::AccessDenied, + _ => IronRdpErrorKind::Generic, + }; + let repr = value.report().to_string(); + make_ffi_error(repr, kind) + } +} + +impl From for Box { + fn from(value: SessionError) -> Self { + let kind = match value.kind() { + ironrdp::session::SessionErrorKind::Pdu(_) => IronRdpErrorKind::PduError, + ironrdp::session::SessionErrorKind::Encode(_) => IronRdpErrorKind::EncodeError, + ironrdp::session::SessionErrorKind::Decode(_) => IronRdpErrorKind::DecodeError, + _ => IronRdpErrorKind::Generic, + }; + let repr = value.report().to_string(); + make_ffi_error(repr, kind) + } +} + +impl From for Box { + fn from(value: ironrdp::pdu::PduError) -> Self { + let repr = value.report().to_string(); + make_ffi_error(repr, IronRdpErrorKind::PduError) + } +} + +impl From for Box { + fn from(value: ironrdp::core::EncodeError) -> Self { + let repr = value.report().to_string(); + make_ffi_error(repr, IronRdpErrorKind::EncodeError) + } +} + +impl From for Box { + fn from(value: ironrdp::core::DecodeError) -> Self { + let repr = value.report().to_string(); + make_ffi_error(repr, IronRdpErrorKind::DecodeError) + } +} + +// std::io::Error - convert to anyhow::Error for proper source chain formatting +impl From for Box { + fn from(value: std::io::Error) -> Self { + let repr = format!("{:#}", anyhow::Error::new(value)); + make_ffi_error(repr, IronRdpErrorKind::IO) + } +} + +// sspi::Error - convert to anyhow::Error for proper source chain formatting +impl From for Box { + fn from(value: sspi::Error) -> Self { + let repr = format!("{:#}", anyhow::Error::new(value)); + make_ffi_error(repr, IronRdpErrorKind::CredsspError) + } +} + +// Simple string error +impl From<&str> for Box { + fn from(value: &str) -> Self { + make_ffi_error(value.to_owned(), IronRdpErrorKind::Generic) + } +} + +// core::fmt::Error - convert to anyhow::Error for consistency +impl From for Box { + fn from(value: core::fmt::Error) -> Self { + let repr = format!("{:#}", anyhow::Error::new(value)); + make_ffi_error(repr, IronRdpErrorKind::Generic) + } +} + +// Clipboard errors - manually format with full source chain +impl From<&dyn ClipboardError> for Box { + fn from(value: &dyn ClipboardError) -> Self { + use core::fmt::Write as _; + + // Manually build error chain since we have a trait object reference + let mut repr = value.to_string(); + let mut source = value.source(); + while let Some(e) = source { + let _ = write!(&mut repr, ", caused by: {e}"); + source = e.source(); + } + make_ffi_error(repr, IronRdpErrorKind::Clipboard) + } +} + +#[cfg(target_os = "windows")] +impl From for Box { + fn from(value: WinCliprdrError) -> Self { + let repr = format!("{:#}", anyhow::Error::new(value)); + make_ffi_error(repr, IronRdpErrorKind::Clipboard) + } +} + +// DER errors - convert to anyhow::Error for proper source chain formatting +impl From for Box { + fn from(value: der::Error) -> Self { + let repr = format!("{:#}", anyhow::Error::new(value)); + make_ffi_error(repr, IronRdpErrorKind::DecodeError) + } +} + +impl From for Box { + fn from(value: ironrdp_rdcleanpath::MissingRDCleanPathField) -> Self { + let repr = format!("{:#}", anyhow::Error::new(value)); + make_ffi_error(repr, IronRdpErrorKind::Generic) + } +} + +// GenericError already has proper Display impl with {:#} +impl From for Box { + fn from(value: GenericError) -> Self { + make_ffi_error(value.to_string(), IronRdpErrorKind::Generic) + } +} + +// FFI-specific errors +impl From for Box { + fn from(value: ValueConsumedError) -> Self { + make_ffi_error(value.to_string(), IronRdpErrorKind::Consumed) + } +} + +impl From for Box { + fn from(value: IncorrectEnumTypeError) -> Self { + make_ffi_error(value.to_string(), IronRdpErrorKind::IncorrectEnumType) + } +} + +impl From for Box { + fn from(value: WrongOSError) -> Self { + make_ffi_error(value.to_string(), IronRdpErrorKind::WrongOS) + } +} + #[diplomat::bridge] pub mod ffi { use core::fmt::Write as _; From f31af061b99ec984709186cf88e76bead839d22a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 00:17:45 -0500 Subject: [PATCH 45/51] build(deps): bump rstest from 0.25.0 to 0.26.1 (#1052) --- Cargo.lock | 9 ++++----- Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3312e0dd..9c344a86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4728,21 +4728,20 @@ dependencies = [ [[package]] name = "rstest" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fc39292f8613e913f7df8fa892b8944ceb47c247b78e1b1ae2f09e019be789d" +checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" dependencies = [ "futures-timer", "futures-util", "rstest_macros", - "rustc_version", ] [[package]] name = "rstest_macros" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f168d99749d307be9de54d23fd226628d99768225ef08f6ffb52e0182a27746" +checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" dependencies = [ "cfg-if", "glob", diff --git a/Cargo.toml b/Cargo.toml index 203c1be7..72904a94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ categories = ["network-programming"] # even for private dependencies. expect-test = "1" proptest = "1.4" -rstest = "0.25" +rstest = "0.26" # Note: we are trying to move away from using these crates. # They are being kept around for now for legacy compatibility, From 0903c9ae753bc62af0aa1f46fbcddc753c251898 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 00:18:06 -0500 Subject: [PATCH 46/51] build(deps): bump the patch group across 1 directory with 4 updates (#1051) --- Cargo.lock | 24 ++++++++++++------------ fuzz/Cargo.lock | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9c344a86..2f75f855 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -949,9 +949,9 @@ dependencies = [ [[package]] name = "criterion" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0dfe5e9e71bdcf4e4954f7d14da74d1cdb92a3a07686452d1509652684b1aab" +checksum = "4d883447757bb0ee46f233e9dc22eb84d93a9508c9b868687b274fc431d886bf" dependencies = [ "alloca", "anes", @@ -974,9 +974,9 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de36c2bee19fba779808f92bf5d9b0fa5a40095c277aba10c458a12b35d21d6" +checksum = "ed943f81ea2faa8dcecbbfa50164acf95d555afec96a27871663b300e387b2e4" dependencies = [ "cast", "itertools", @@ -2193,9 +2193,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ "base64", "bytes", @@ -3186,9 +3186,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "lru-slab" @@ -4616,9 +4616,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "reqwest" -version = "0.12.24" +version = "0.12.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" +checksum = "b6eff9328d40131d43bd911d42d79eb6a47312002a4daefc9e37f17e74a7701a" dependencies = [ "base64", "bytes", @@ -5825,9 +5825,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "bitflags 2.10.0", "bytes", diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 2a1fd15c..8eac17ca 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -458,9 +458,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "md-5" From d587b0c4c114c49d30f52859f43b22f829456a01 Mon Sep 17 00:00:00 2001 From: glamberson Date: Thu, 11 Dec 2025 16:49:07 +0000 Subject: [PATCH 47/51] fix(cliprdr): allow servers to announce clipboard ownership (#1053) Servers can now send Format List PDU via initiate_copy() regardless of internal state. The existing state machine was designed for clients where clipboard initialization must complete before announcing ownership. MS-RDPECLIP Section 2.2.3.1 specifies that Format List PDU is sent by either client or server when the local clipboard is updated. Servers should be able to announce clipboard changes immediately after channel negotiation. This change enables RDP servers to properly announce clipboard ownership by bypassing the Initialization/Ready state check when R::is_server() is true. Client behavior remains unchanged. Co-authored-by: lamco-office --- crates/ironrdp-cliprdr/src/lib.rs | 46 +++++++++++++++++-------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/crates/ironrdp-cliprdr/src/lib.rs b/crates/ironrdp-cliprdr/src/lib.rs index 1c0d2143..41f5d62c 100644 --- a/crates/ironrdp-cliprdr/src/lib.rs +++ b/crates/ironrdp-cliprdr/src/lib.rs @@ -230,26 +230,32 @@ impl Cliprdr { pub fn initiate_copy(&self, available_formats: &[ClipboardFormat]) -> PduResult> { let mut pdus = Vec::new(); - match (self.state, R::is_server()) { - // When user initiates copy, we should send format list to server. - (CliprdrState::Ready, _) => { - pdus.push(ClipboardPdu::FormatList( - self.build_format_list(available_formats).map_err(|e| encode_err!(e))?, - )); - } - (CliprdrState::Initialization, false) => { - // During initialization state, first copy action is synthetic and should be sent along with - // capabilities and temporary directory PDUs. - pdus.push(ClipboardPdu::Capabilities(self.capabilities.clone())); - pdus.push(ClipboardPdu::TemporaryDirectory( - ClientTemporaryDirectory::new(self.backend.temporary_directory()).map_err(|e| encode_err!(e))?, - )); - pdus.push(ClipboardPdu::FormatList( - self.build_format_list(available_formats).map_err(|e| encode_err!(e))?, - )); - } - _ => { - error!(?self.state, "Attempted to initiate copy in incorrect state"); + if R::is_server() { + pdus.push(ClipboardPdu::FormatList( + self.build_format_list(available_formats).map_err(|e| encode_err!(e))?, + )); + } else { + match self.state { + CliprdrState::Ready => { + pdus.push(ClipboardPdu::FormatList( + self.build_format_list(available_formats).map_err(|e| encode_err!(e))?, + )); + } + CliprdrState::Initialization => { + // During initialization state, first copy action is synthetic and should be sent along with + // capabilities and temporary directory PDUs. + pdus.push(ClipboardPdu::Capabilities(self.capabilities.clone())); + pdus.push(ClipboardPdu::TemporaryDirectory( + ClientTemporaryDirectory::new(self.backend.temporary_directory()) + .map_err(|e| encode_err!(e))?, + )); + pdus.push(ClipboardPdu::FormatList( + self.build_format_list(available_formats).map_err(|e| encode_err!(e))?, + )); + } + _ => { + error!(?self.state, "Attempted to initiate copy in incorrect state"); + } } } From 113284a0539a6b1e232a7957d877a3593d82252a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 02:09:12 -0500 Subject: [PATCH 48/51] build(deps): bump uuid from 1.18.1 to 1.19.0 (#1056) --- Cargo.lock | 6 +++--- crates/ironrdp-client/Cargo.toml | 2 +- crates/ironrdp-mstsgu/Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f75f855..d6debd8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6054,13 +6054,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "getrandom 0.3.4", "js-sys", - "serde", + "serde_core", "wasm-bindgen", ] diff --git a/crates/ironrdp-client/Cargo.toml b/crates/ironrdp-client/Cargo.toml index 5b6ab1b3..6cff8a9b 100644 --- a/crates/ironrdp-client/Cargo.toml +++ b/crates/ironrdp-client/Cargo.toml @@ -83,7 +83,7 @@ smallvec = "1.15" tap = "1" semver = "1" raw-window-handle = "0.6" -uuid = { version = "1.18" } +uuid = { version = "1.19" } x509-cert = { version = "0.2", default-features = false, features = ["std"] } url = "2" diff --git a/crates/ironrdp-mstsgu/Cargo.toml b/crates/ironrdp-mstsgu/Cargo.toml index 988fb3a4..d5750df0 100644 --- a/crates/ironrdp-mstsgu/Cargo.toml +++ b/crates/ironrdp-mstsgu/Cargo.toml @@ -35,7 +35,7 @@ log = "0.4" tokio-tungstenite = { version = "0.28" } tokio-util = { version = "0.7" } tokio = { version = "1.43", features = ["macros", "rt"] } -uuid = { version = "1.16", features = ["v4"] } +uuid = { version = "1.19", features = ["v4"] } [lints] workspace = true From b50b6483442b80866d89fdf740daa0f55bb35e74 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 02:09:49 -0500 Subject: [PATCH 49/51] build(deps): bump the patch group across 1 directory with 3 updates (#1055) --- Cargo.lock | 263 ++++++++++++++++++----------------------------------- 1 file changed, 88 insertions(+), 175 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d6debd8e..26bba1ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -658,7 +658,7 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -849,20 +849,7 @@ checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", "core-foundation 0.9.4", - "core-graphics-types 0.1.3", - "foreign-types 0.5.0", - "libc", -] - -[[package]] -name = "core-graphics" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" -dependencies = [ - "bitflags 2.10.0", - "core-foundation 0.10.1", - "core-graphics-types 0.2.0", + "core-graphics-types", "foreign-types 0.5.0", "libc", ] @@ -878,17 +865,6 @@ dependencies = [ "libc", ] -[[package]] -name = "core-graphics-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" -dependencies = [ - "bitflags 2.10.0", - "core-foundation 0.10.1", - "libc", -] - [[package]] name = "coreaudio-rs" version = "0.13.0" @@ -1415,22 +1391,23 @@ checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] name = "drm" -version = "0.12.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1" +checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f" dependencies = [ "bitflags 2.10.0", "bytemuck", "drm-ffi", "drm-fourcc", + "libc", "rustix 0.38.44", ] [[package]] name = "drm-ffi" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97c98727e48b7ccb4f4aea8cfe881e5b07f702d17b7875991881b41af7278d53" +checksum = "d8e41459d99a9b529845f6d2c909eb9adf3b6d2f82635ae40be8de0601726e8b" dependencies = [ "drm-sys", "rustix 0.38.44", @@ -1444,9 +1421,9 @@ checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" [[package]] name = "drm-sys" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd39dde40b6e196c2e8763f23d119ddb1a8714534bf7d77fa97a65b0feda3986" +checksum = "bafb66c8dbc944d69e15cfcc661df7e703beffbaec8bd63151368b06c5f9858c" dependencies = [ "libc", "linux-raw-sys 0.6.5", @@ -1846,7 +1823,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ "rustix 1.1.2", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -2214,7 +2191,7 @@ dependencies = [ "tokio", "tower-service", "tracing", - "windows-registry 0.6.1", + "windows-registry", ] [[package]] @@ -3103,7 +3080,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -3497,7 +3474,7 @@ dependencies = [ "objc2-core-data", "objc2-core-image", "objc2-foundation 0.2.2", - "objc2-quartz-core", + "objc2-quartz-core 0.2.2", ] [[package]] @@ -3584,6 +3561,19 @@ dependencies = [ "objc2 0.6.3", ] +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.10.0", + "dispatch2", + "objc2 0.6.3", + "objc2-core-foundation", + "objc2-io-surface", +] + [[package]] name = "objc2-core-image" version = "0.2.2" @@ -3633,7 +3623,20 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ + "bitflags 2.10.0", "objc2 0.6.3", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.10.0", + "objc2 0.6.3", + "objc2-core-foundation", ] [[package]] @@ -3673,6 +3676,18 @@ dependencies = [ "objc2-metal", ] +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.10.0", + "objc2 0.6.3", + "objc2-core-foundation", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-symbols" version = "0.2.2" @@ -3698,7 +3713,7 @@ dependencies = [ "objc2-core-location", "objc2-foundation 0.2.2", "objc2-link-presentation", - "objc2-quartz-core", + "objc2-quartz-core 0.2.2", "objc2-symbols", "objc2-uniform-type-identifiers", "objc2-user-notifications", @@ -3902,7 +3917,7 @@ dependencies = [ "libc", "redox_syscall 0.5.18", "smallvec", - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -4057,9 +4072,9 @@ dependencies = [ [[package]] name = "picky-krb" -version = "0.11.3" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b13eb1a97b2293277b475f07d0c36c33579e2e71f852557015addcd95f8892" +checksum = "9ed61c8d7448649c031ecae02afb10c679524c7a9af5fb0fbee466b3cc0d6df1" dependencies = [ "aes", "block-buffer 0.11.0-rc.5", @@ -4616,9 +4631,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "reqwest" -version = "0.12.25" +version = "0.12.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6eff9328d40131d43bd911d42d79eb6a47312002a4daefc9e37f17e74a7701a" +checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" dependencies = [ "base64", "bytes", @@ -5251,33 +5266,33 @@ dependencies = [ [[package]] name = "softbuffer" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" +checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" dependencies = [ "as-raw-xcb-connection", "bytemuck", - "cfg_aliases", - "core-graphics 0.24.0", "drm", "fastrand", - "foreign-types 0.5.0", "js-sys", - "log", "memmap2", - "objc2 0.5.2", - "objc2-foundation 0.2.2", - "objc2-quartz-core", + "ndk", + "objc2 0.6.3", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", "raw-window-handle", "redox_syscall 0.5.18", - "rustix 0.38.44", + "rustix 1.1.2", "tiny-xlib", + "tracing", "wasm-bindgen", "wayland-backend", "wayland-client", "wayland-sys", "web-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", "x11rb", ] @@ -5312,9 +5327,9 @@ dependencies = [ [[package]] name = "sspi" -version = "0.18.4" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f8d436177e1f17d2efb8ec1b661068ad04fd282544dfd8369522168300a82e" +checksum = "43f73fe6be958ae27fa8e982d9acc42d16f34eb74714d95bb53015528667cae4" dependencies = [ "async-dnssd", "async-recursion", @@ -5363,7 +5378,6 @@ dependencies = [ "rustls", "rustls-native-certs", "serde", - "serde_derive", "sha1 0.11.0-rc.2", "sha2", "signature", @@ -5373,9 +5387,8 @@ dependencies = [ "tracing", "url", "uuid", - "windows 0.61.3", - "windows-registry 0.5.3", - "windows-sys 0.60.2", + "windows 0.62.2", + "windows-registry", "winscard", "zeroize", ] @@ -6416,38 +6429,16 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows" -version = "0.61.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" -dependencies = [ - "windows-collections 0.2.0", - "windows-core 0.61.2", - "windows-future 0.2.1", - "windows-link 0.1.3", - "windows-numerics 0.2.0", -] - [[package]] name = "windows" version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ - "windows-collections 0.3.2", + "windows-collections", "windows-core 0.62.2", - "windows-future 0.3.2", - "windows-numerics 0.3.1", -] - -[[package]] -name = "windows-collections" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" -dependencies = [ - "windows-core 0.61.2", + "windows-future", + "windows-numerics", ] [[package]] @@ -6469,19 +6460,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", -] - [[package]] name = "windows-core" version = "0.62.2" @@ -6490,20 +6468,9 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.2.1", + "windows-link", "windows-result 0.4.1", - "windows-strings 0.5.1", -] - -[[package]] -name = "windows-future" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", - "windows-threading 0.1.0", + "windows-strings", ] [[package]] @@ -6513,8 +6480,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ "windows-core 0.62.2", - "windows-link 0.2.1", - "windows-threading 0.2.1", + "windows-link", + "windows-threading", ] [[package]] @@ -6539,28 +6506,12 @@ dependencies = [ "syn", ] -[[package]] -name = "windows-link" -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-numerics" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" -dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", -] - [[package]] name = "windows-numerics" version = "0.3.1" @@ -6568,18 +6519,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ "windows-core 0.62.2", - "windows-link 0.2.1", -] - -[[package]] -name = "windows-registry" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" -dependencies = [ - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", + "windows-link", ] [[package]] @@ -6588,9 +6528,9 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ - "windows-link 0.2.1", + "windows-link", "windows-result 0.4.1", - "windows-strings 0.5.1", + "windows-strings", ] [[package]] @@ -6602,31 +6542,13 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link 0.1.3", -] - [[package]] name = "windows-result" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -6635,7 +6557,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -6689,7 +6611,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -6744,7 +6666,7 @@ version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link 0.2.1", + "windows-link", "windows_aarch64_gnullvm 0.53.1", "windows_aarch64_msvc 0.53.1", "windows_i686_gnu 0.53.1", @@ -6755,22 +6677,13 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] -[[package]] -name = "windows-threading" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" -dependencies = [ - "windows-link 0.1.3", -] - [[package]] name = "windows-threading" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -6969,7 +6882,7 @@ dependencies = [ "cfg_aliases", "concurrent-queue", "core-foundation 0.9.4", - "core-graphics 0.23.2", + "core-graphics", "cursor-icon", "dpi", "js-sys", From bd2aed76867f4038c32df9a0d24532ee40d2f14c Mon Sep 17 00:00:00 2001 From: Will Warner <93586455+willwwarner@users.noreply.github.com> Date: Wed, 17 Dec 2025 23:14:51 -0500 Subject: [PATCH 50/51] feat(ironrdp-tls)!: return x509_cert::Certificate from upgrade() (#1054) This allows client applications to verify details of the certificate, possibly with the user, when connecting to a server using TLS. --- crates/ironrdp-client/src/rdp.rs | 6 ++++-- crates/ironrdp-testsuite-extra/tests/mod.rs | 6 ++++-- crates/ironrdp-tls/Cargo.toml | 2 +- crates/ironrdp-tls/src/lib.rs | 16 ++-------------- crates/ironrdp-tls/src/native_tls.rs | 11 +++++++---- crates/ironrdp-tls/src/rustls.rs | 11 +++++++---- 6 files changed, 25 insertions(+), 27 deletions(-) diff --git a/crates/ironrdp-client/src/rdp.rs b/crates/ironrdp-client/src/rdp.rs index 23f5c0ea..a6693fba 100644 --- a/crates/ironrdp-client/src/rdp.rs +++ b/crates/ironrdp-client/src/rdp.rs @@ -229,7 +229,7 @@ async fn connect( // Ensure there is no leftover let (initial_stream, leftover_bytes) = framed.into_inner(); - let (upgraded_stream, server_public_key) = ironrdp_tls::upgrade(initial_stream, config.destination.name()) + let (upgraded_stream, tls_cert) = ironrdp_tls::upgrade(initial_stream, config.destination.name()) .await .map_err(|e| connector::custom_err!("TLS upgrade", e))?; @@ -238,13 +238,15 @@ async fn connect( let erased_stream: Box = Box::new(upgraded_stream); let mut upgraded_framed = ironrdp_tokio::TokioFramed::new_with_leftover(erased_stream, leftover_bytes); + let server_public_key = ironrdp_tls::extract_tls_server_public_key(&tls_cert) + .ok_or_else(|| connector::general_err!("unable to extract tls server public key"))?; let connection_result = ironrdp_tokio::connect_finalize( upgraded, connector, &mut upgraded_framed, &mut ReqwestNetworkClient::new(), (&config.destination).into(), - server_public_key, + server_public_key.to_owned(), None, ) .await?; diff --git a/crates/ironrdp-testsuite-extra/tests/mod.rs b/crates/ironrdp-testsuite-extra/tests/mod.rs index e5bade18..8cc24fab 100644 --- a/crates/ironrdp-testsuite-extra/tests/mod.rs +++ b/crates/ironrdp-testsuite-extra/tests/mod.rs @@ -205,18 +205,20 @@ where .await .expect("begin connection"); let initial_stream = framed.into_inner_no_leftover(); - let (upgraded_stream, server_public_key) = ironrdp_tls::upgrade(initial_stream, "localhost") + let (upgraded_stream, tls_cert) = ironrdp_tls::upgrade(initial_stream, "localhost") .await .expect("TLS upgrade"); let upgraded = ironrdp_tokio::mark_as_upgraded(should_upgrade, &mut connector); let mut upgraded_framed = ironrdp_tokio::TokioFramed::new(upgraded_stream); + let server_public_key = + ironrdp_tls::extract_tls_server_public_key(&tls_cert).expect("extract server public key"); let connection_result = ironrdp_async::connect_finalize( upgraded, connector, &mut upgraded_framed, &mut ironrdp_tokio::reqwest::ReqwestNetworkClient::new(), "localhost".into(), - server_public_key, + server_public_key.to_owned(), None, ) .await diff --git a/crates/ironrdp-tls/Cargo.toml b/crates/ironrdp-tls/Cargo.toml index 55556ffd..1bfdf571 100644 --- a/crates/ironrdp-tls/Cargo.toml +++ b/crates/ironrdp-tls/Cargo.toml @@ -23,7 +23,7 @@ stub = [] [dependencies] tokio = { version = "1.47" } -x509-cert = { version = "0.2", default-features = false, features = ["std"], optional = true } +x509-cert = { version = "0.2", default-features = false, features = ["std"], optional = true } # public tokio-native-tls = { version = "0.3", optional = true } # public tokio-rustls = { version = "0.26", optional = true } # public diff --git a/crates/ironrdp-tls/src/lib.rs b/crates/ironrdp-tls/src/lib.rs index 63261890..58349712 100644 --- a/crates/ironrdp-tls/src/lib.rs +++ b/crates/ironrdp-tls/src/lib.rs @@ -25,21 +25,9 @@ compile_error!("a TLS backend must be selected by enabling a single feature out #[cfg(any(feature = "stub", feature = "native-tls", feature = "rustls"))] pub use impl_::{upgrade, TlsStream}; -#[cfg(any(feature = "native-tls", feature = "rustls"))] -pub(crate) fn extract_tls_server_public_key(cert: &[u8]) -> std::io::Result> { - use std::io; - - use x509_cert::der::Decode as _; - - let cert = x509_cert::Certificate::from_der(cert).map_err(io::Error::other)?; - - let server_public_key = cert - .tbs_certificate +pub fn extract_tls_server_public_key(cert: &x509_cert::Certificate) -> Option<&[u8]> { + cert.tbs_certificate .subject_public_key_info .subject_public_key .as_bytes() - .ok_or_else(|| io::Error::other("subject public key BIT STRING is not aligned"))? - .to_owned(); - - Ok(server_public_key) } diff --git a/crates/ironrdp-tls/src/native_tls.rs b/crates/ironrdp-tls/src/native_tls.rs index 0e652e7e..f3b7d0d0 100644 --- a/crates/ironrdp-tls/src/native_tls.rs +++ b/crates/ironrdp-tls/src/native_tls.rs @@ -4,7 +4,7 @@ use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt as _}; pub type TlsStream = tokio_native_tls::TlsStream; -pub async fn upgrade(stream: S, server_name: &str) -> io::Result<(TlsStream, Vec)> +pub async fn upgrade(stream: S, server_name: &str) -> io::Result<(TlsStream, x509_cert::Certificate)> where S: Unpin + AsyncRead + AsyncWrite, { @@ -24,15 +24,18 @@ where tls_stream.flush().await?; - let server_public_key = { + let tls_cert = { + use x509_cert::der::Decode as _; + let cert = tls_stream .get_ref() .peer_certificate() .map_err(|e| io::Error::new(io::ErrorKind::Other, e))? .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "peer certificate is missing"))?; let cert = cert.to_der().map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; - crate::extract_tls_server_public_key(&cert)? + + x509_cert::Certificate::from_der(&cert).map_err(io::Error::other)? }; - Ok((tls_stream, server_public_key)) + Ok((tls_stream, tls_cert)) } diff --git a/crates/ironrdp-tls/src/rustls.rs b/crates/ironrdp-tls/src/rustls.rs index ea3aa3c0..ca8778d5 100644 --- a/crates/ironrdp-tls/src/rustls.rs +++ b/crates/ironrdp-tls/src/rustls.rs @@ -6,7 +6,7 @@ use tokio_rustls::rustls::pki_types::ServerName; pub type TlsStream = tokio_rustls::client::TlsStream; -pub async fn upgrade(stream: S, server_name: &str) -> io::Result<(TlsStream, Vec)> +pub async fn upgrade(stream: S, server_name: &str) -> io::Result<(TlsStream, x509_cert::Certificate)> where S: Unpin + AsyncRead + AsyncWrite, { @@ -35,17 +35,20 @@ where tls_stream.flush().await?; - let server_public_key = { + let tls_cert = { + use x509_cert::der::Decode as _; + let cert = tls_stream .get_ref() .1 .peer_certificates() .and_then(|certificates| certificates.first()) .ok_or_else(|| io::Error::other("peer certificate is missing"))?; - crate::extract_tls_server_public_key(cert)? + + x509_cert::Certificate::from_der(cert).map_err(io::Error::other)? }; - Ok((tls_stream, server_public_key)) + Ok((tls_stream, tls_cert)) } mod danger { From 87f8d073c83a6cdd9bf1914b7b7ec8d9f5a551d3 Mon Sep 17 00:00:00 2001 From: devolutionsbot <31221910+devolutionsbot@users.noreply.github.com> Date: Sat, 20 Dec 2025 06:42:23 -0500 Subject: [PATCH 51/51] chore(release): prepare for publishing (#1020) --- Cargo.lock | 228 +++++++++------------ crates/ironrdp-acceptor/CHANGELOG.md | 11 + crates/ironrdp-acceptor/Cargo.toml | 6 +- crates/ironrdp-async/CHANGELOG.md | 13 +- crates/ironrdp-async/Cargo.toml | 4 +- crates/ironrdp-blocking/CHANGELOG.md | 13 +- crates/ironrdp-blocking/Cargo.toml | 4 +- crates/ironrdp-client/Cargo.toml | 8 +- crates/ironrdp-cliprdr-native/CHANGELOG.md | 16 ++ crates/ironrdp-cliprdr-native/Cargo.toml | 4 +- crates/ironrdp-cliprdr/CHANGELOG.md | 24 +++ crates/ironrdp-cliprdr/Cargo.toml | 2 +- crates/ironrdp-connector/CHANGELOG.md | 8 + crates/ironrdp-connector/Cargo.toml | 2 +- crates/ironrdp-futures/CHANGELOG.md | 4 +- crates/ironrdp-futures/Cargo.toml | 4 +- crates/ironrdp-graphics/CHANGELOG.md | 10 + crates/ironrdp-graphics/Cargo.toml | 2 +- crates/ironrdp-mstsgu/Cargo.toml | 2 +- crates/ironrdp-rdpdr-native/CHANGELOG.md | 3 + crates/ironrdp-rdpdr-native/Cargo.toml | 4 +- crates/ironrdp-rdpdr/CHANGELOG.md | 17 +- crates/ironrdp-rdpdr/Cargo.toml | 2 +- crates/ironrdp-rdpsnd-native/CHANGELOG.md | 7 +- crates/ironrdp-rdpsnd-native/Cargo.toml | 2 +- crates/ironrdp-server/CHANGELOG.md | 9 + crates/ironrdp-server/Cargo.toml | 12 +- crates/ironrdp-session/CHANGELOG.md | 3 + crates/ironrdp-session/Cargo.toml | 6 +- crates/ironrdp-tls/CHANGELOG.md | 9 + crates/ironrdp-tls/Cargo.toml | 2 +- crates/ironrdp-tokio/CHANGELOG.md | 43 ++++ crates/ironrdp-tokio/Cargo.toml | 6 +- crates/ironrdp/CHANGELOG.md | 8 + crates/ironrdp/Cargo.toml | 20 +- fuzz/Cargo.lock | 62 +++--- 36 files changed, 357 insertions(+), 223 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 26bba1ef..caaee2ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -355,9 +355,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.15.0" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5932a7d9d28b0d2ea34c6b3779d35e3dd6f6345317c34e73438c4f1f29144151" +checksum = "6a88aab2464f1f25453baa7a07c84c5b7684e274054ba06817f382357f77a288" dependencies = [ "aws-lc-sys", "zeroize", @@ -365,11 +365,10 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.33.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1826f2e4cfc2cd19ee53c42fbf68e2f81ec21108e0b7ecf6a71cf062137360fc" +checksum = "b45afffdee1e7c9126814751f88dddc747f41d91da16c9551a0f1e8a11e788a1" dependencies = [ - "bindgen", "cc", "cmake", "dunce", @@ -390,9 +389,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" [[package]] name = "benches" @@ -408,26 +407,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "bindgen" -version = "0.72.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" -dependencies = [ - "bitflags 2.10.0", - "cexpr", - "clang-sys", - "itertools", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn", -] - [[package]] name = "bit-set" version = "0.8.0" @@ -520,9 +499,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "bytemuck" @@ -611,9 +590,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.46" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ "find-msvc-tools", "jobserver", @@ -627,15 +606,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" version = "1.0.4" @@ -699,17 +669,6 @@ dependencies = [ "inout", ] -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" version = "4.5.53" @@ -752,9 +711,9 @@ checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "cmake" -version = "0.1.54" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" dependencies = [ "cc", ] @@ -808,9 +767,9 @@ checksum = "0dabb6555f92fb9ee4140454eb5dcd14c7960e1225c6d1a6cc361f032947713e" [[package]] name = "convert_case" -version = "0.7.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" dependencies = [ "unicode-segmentation", ] @@ -1239,22 +1198,23 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" dependencies = [ "convert_case", "proc-macro2", "quote", + "rustc_version", "syn", ] @@ -1963,9 +1923,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "heapless" @@ -2064,12 +2024,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -2266,9 +2225,9 @@ checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ "icu_collections", "icu_locale_core", @@ -2280,9 +2239,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" @@ -2335,9 +2294,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", "hashbrown", @@ -2409,7 +2368,7 @@ dependencies = [ [[package]] name = "ironrdp" -version = "0.13.0" +version = "0.14.0" dependencies = [ "anyhow", "async-trait", @@ -2442,7 +2401,7 @@ dependencies = [ [[package]] name = "ironrdp-acceptor" -version = "0.7.0" +version = "0.8.0" dependencies = [ "ironrdp-async", "ironrdp-connector", @@ -2465,7 +2424,7 @@ dependencies = [ [[package]] name = "ironrdp-async" -version = "0.7.0" +version = "0.8.0" dependencies = [ "bytes", "ironrdp-connector", @@ -2486,7 +2445,7 @@ dependencies = [ [[package]] name = "ironrdp-blocking" -version = "0.7.0" +version = "0.8.0" dependencies = [ "bytes", "ironrdp-connector", @@ -2544,7 +2503,7 @@ dependencies = [ [[package]] name = "ironrdp-cliprdr" -version = "0.4.0" +version = "0.5.0" dependencies = [ "bitflags 2.10.0", "ironrdp-core", @@ -2563,7 +2522,7 @@ dependencies = [ [[package]] name = "ironrdp-cliprdr-native" -version = "0.4.0" +version = "0.5.0" dependencies = [ "ironrdp-cliprdr", "ironrdp-core", @@ -2573,7 +2532,7 @@ dependencies = [ [[package]] name = "ironrdp-connector" -version = "0.7.1" +version = "0.8.0" dependencies = [ "arbitrary", "ironrdp-core", @@ -2637,7 +2596,7 @@ version = "0.1.3" [[package]] name = "ironrdp-futures" -version = "0.5.0" +version = "0.6.0" dependencies = [ "bytes", "futures-util", @@ -2662,7 +2621,7 @@ dependencies = [ [[package]] name = "ironrdp-graphics" -version = "0.6.0" +version = "0.7.0" dependencies = [ "bit_field", "bitflags 2.10.0", @@ -2750,7 +2709,7 @@ dependencies = [ [[package]] name = "ironrdp-rdpdr" -version = "0.4.1" +version = "0.5.0" dependencies = [ "bitflags 2.10.0", "ironrdp-core", @@ -2762,7 +2721,7 @@ dependencies = [ [[package]] name = "ironrdp-rdpdr-native" -version = "0.4.0" +version = "0.5.0" dependencies = [ "ironrdp-core", "ironrdp-pdu", @@ -2792,7 +2751,7 @@ dependencies = [ [[package]] name = "ironrdp-rdpsnd-native" -version = "0.4.1" +version = "0.4.2" dependencies = [ "anyhow", "bytemuck", @@ -2805,7 +2764,7 @@ dependencies = [ [[package]] name = "ironrdp-server" -version = "0.9.0" +version = "0.10.0" dependencies = [ "anyhow", "async-trait", @@ -2835,7 +2794,7 @@ dependencies = [ [[package]] name = "ironrdp-session" -version = "0.7.0" +version = "0.8.0" dependencies = [ "ironrdp-connector", "ironrdp-core", @@ -2912,7 +2871,7 @@ dependencies = [ [[package]] name = "ironrdp-tls" -version = "0.1.4" +version = "0.2.0" dependencies = [ "tokio", "tokio-native-tls", @@ -2922,7 +2881,7 @@ dependencies = [ [[package]] name = "ironrdp-tokio" -version = "0.7.0" +version = "0.8.0" dependencies = [ "bytes", "ironrdp-async", @@ -3069,9 +3028,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.177" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libloading" @@ -3102,13 +3061,13 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" dependencies = [ "bitflags 2.10.0", "libc", - "redox_syscall 0.5.18", + "redox_syscall 0.6.0", ] [[package]] @@ -3253,9 +3212,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "log", @@ -3283,9 +3242,9 @@ dependencies = [ [[package]] name = "moxcms" -version = "0.7.9" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fbdd3d7436f8b5e892b8b7ea114271ff0fa00bc5acae845d53b07d498616ef6" +checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" dependencies = [ "num-traits", "pxfm", @@ -4289,16 +4248,6 @@ dependencies = [ "yansi", ] -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn", -] - [[package]] name = "primefield" version = "0.14.0-pre.6" @@ -4366,9 +4315,9 @@ dependencies = [ [[package]] name = "pxfm" -version = "0.1.25" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3cbdf373972bf78df4d3b518d07003938e2c7d1fb5891e55f9cb6df57009d84" +checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8" dependencies = [ "num-traits", ] @@ -4594,6 +4543,15 @@ dependencies = [ "bitflags 2.10.0", ] +[[package]] +name = "redox_syscall" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5" +dependencies = [ + "bitflags 2.10.0", +] + [[package]] name = "regex" version = "1.12.2" @@ -4859,9 +4817,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" dependencies = [ "web-time", "zeroize", @@ -5065,9 +5023,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" dependencies = [ "serde_core", ] @@ -5175,9 +5133,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" dependencies = [ "libc", ] @@ -5194,9 +5152,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "slab" @@ -5435,9 +5393,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.110" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -5772,9 +5730,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.8" +version = "0.9.9+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +checksum = "eb5238e643fc34a1d5d7e753e1532a91912d74b63b92b3ea51fde8d1b7bc79dd" dependencies = [ "indexmap", "serde_core", @@ -5787,18 +5745,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.3" +version = "0.7.4+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +checksum = "fe3cea6b2aa3b910092f6abd4053ea464fab5f9c170ba5e9a6aead16ec4af2b6" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.23.7" +version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ "indexmap", "toml_datetime", @@ -5808,18 +5766,18 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.4" +version = "1.0.5+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +checksum = "4c03bee5ce3696f31250db0bbaff18bc43301ce0e8db2ed1f07cbb2acf89984c" dependencies = [ "winnow", ] [[package]] name = "toml_writer" -version = "1.0.4" +version = "1.0.5+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" +checksum = "a9cd6190959dce0994aa8970cd32ab116d1851ead27e866039acaf2524ce44fa" [[package]] name = "tower" @@ -6920,9 +6878,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -6949,9 +6907,9 @@ dependencies = [ [[package]] name = "winscard" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a17695372a560c4f47f2e24e7754cdcc138fd6e62b8b54801d69f19a121a1c" +checksum = "73b6ec4e6176df62589d1ac9950f6295be87ca06ee61a7c9a579a2bcc80efe34" dependencies = [ "bitflags 2.10.0", "crypto-bigint", @@ -7137,18 +7095,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", diff --git a/crates/ironrdp-acceptor/CHANGELOG.md b/crates/ironrdp-acceptor/CHANGELOG.md index d915be30..4918e46f 100644 --- a/crates/ironrdp-acceptor/CHANGELOG.md +++ b/crates/ironrdp-acceptor/CHANGELOG.md @@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.8.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-acceptor-v0.7.0...ironrdp-acceptor-v0.8.0)] - 2025-12-18 + +### Bug Fixes + +- [**breaking**] Use static dispatch for NetworkClient trait ([#1043](https://github.com/Devolutions/IronRDP/issues/1043)) ([bca6d190a8](https://github.com/Devolutions/IronRDP/commit/bca6d190a870708468534d224ff225a658767a9a)) + + - Rename `AsyncNetworkClient` to `NetworkClient` + - Replace dynamic dispatch (`Option<&mut dyn ...>`) with static dispatch + using generics (`&mut N where N: NetworkClient`) + - Reorder `connect_finalize` parameters for consistency across crates + ## [[0.6.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-acceptor-v0.5.0...ironrdp-acceptor-v0.6.0)] - 2025-07-08 ### Features diff --git a/crates/ironrdp-acceptor/Cargo.toml b/crates/ironrdp-acceptor/Cargo.toml index a4ef47db..a3c42b9d 100644 --- a/crates/ironrdp-acceptor/Cargo.toml +++ b/crates/ironrdp-acceptor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-acceptor" -version = "0.7.0" +version = "0.8.0" readme = "README.md" description = "State machines to drive an RDP connection acceptance sequence" edition.workspace = true @@ -19,8 +19,8 @@ test = false ironrdp-core = { path = "../ironrdp-core", version = "0.1", features = ["alloc"] } # public ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6" } # public ironrdp-svc = { path = "../ironrdp-svc", version = "0.5" } # public -ironrdp-connector = { path = "../ironrdp-connector", version = "0.7" } # public -ironrdp-async = { path = "../ironrdp-async", version = "0.7" } # public +ironrdp-connector = { path = "../ironrdp-connector", version = "0.8" } # public +ironrdp-async = { path = "../ironrdp-async", version = "0.8" } # public tracing = { version = "0.1", features = ["log"] } [lints] diff --git a/crates/ironrdp-async/CHANGELOG.md b/crates/ironrdp-async/CHANGELOG.md index b83ee99e..d98372c7 100644 --- a/crates/ironrdp-async/CHANGELOG.md +++ b/crates/ironrdp-async/CHANGELOG.md @@ -6,14 +6,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.8.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-async-v0.7.0...ironrdp-async-v0.8.0)] - 2025-12-18 + +### Bug Fixes + +- [**breaking**] Use static dispatch for NetworkClient trait ([#1043](https://github.com/Devolutions/IronRDP/issues/1043)) ([bca6d190a8](https://github.com/Devolutions/IronRDP/commit/bca6d190a870708468534d224ff225a658767a9a)) + + - Rename `AsyncNetworkClient` to `NetworkClient` + - Replace dynamic dispatch (`Option<&mut dyn ...>`) with static dispatch + using generics (`&mut N where N: NetworkClient`) + - Reorder `connect_finalize` parameters for consistency across crates + ## [[0.3.2](https://github.com/Devolutions/IronRDP/compare/ironrdp-async-v0.3.1...ironrdp-async-v0.3.2)] - 2025-03-12 ### Build - Bump ironrdp-pdu - - ## [[0.3.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-async-v0.3.0...ironrdp-async-v0.3.1)] - 2025-03-12 ### Build diff --git a/crates/ironrdp-async/Cargo.toml b/crates/ironrdp-async/Cargo.toml index 31a81d01..1716abb4 100644 --- a/crates/ironrdp-async/Cargo.toml +++ b/crates/ironrdp-async/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-async" -version = "0.7.0" +version = "0.8.0" readme = "README.md" description = "Provides `Future`s wrapping the IronRDP state machines conveniently" edition.workspace = true @@ -16,7 +16,7 @@ doctest = false test = false [dependencies] -ironrdp-connector = { path = "../ironrdp-connector", version = "0.7" } # public +ironrdp-connector = { path = "../ironrdp-connector", version = "0.8" } # public ironrdp-core = { path = "../ironrdp-core", version = "0.1", features = ["alloc"] } # public ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6" } # public tracing = { version = "0.1", features = ["log"] } diff --git a/crates/ironrdp-blocking/CHANGELOG.md b/crates/ironrdp-blocking/CHANGELOG.md index b02d867a..02048123 100644 --- a/crates/ironrdp-blocking/CHANGELOG.md +++ b/crates/ironrdp-blocking/CHANGELOG.md @@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.8.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-blocking-v0.7.0...ironrdp-blocking-v0.8.0)] - 2025-12-18 + +### Bug Fixes + +- [**breaking**] Use static dispatch for NetworkClient trait ([#1043](https://github.com/Devolutions/IronRDP/issues/1043)) ([bca6d190a8](https://github.com/Devolutions/IronRDP/commit/bca6d190a870708468534d224ff225a658767a9a)) + + - Rename `AsyncNetworkClient` to `NetworkClient` + - Replace dynamic dispatch (`Option<&mut dyn ...>`) with static dispatch + using generics (`&mut N where N: NetworkClient`) + - Reorder `connect_finalize` parameters for consistency across crates + ## [[0.4.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-blocking-v0.3.1...ironrdp-blocking-v0.4.0)] - 2025-03-12 ### Build @@ -13,7 +24,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bump ironrdp-pdu - ## [[0.3.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-blocking-v0.3.0...ironrdp-blocking-v0.3.1)] - 2025-03-12 ### Build @@ -31,7 +41,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use CDN URLs instead of the blob storage URLs for Devolutions logo (#631) ([dd249909a8](https://github.com/Devolutions/IronRDP/commit/dd249909a894004d4f728d30b3a4aa77a0f8193b)) - ## [[0.2.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-blocking-v0.2.0...ironrdp-blocking-v0.2.1)] - 2024-12-14 ### Other diff --git a/crates/ironrdp-blocking/Cargo.toml b/crates/ironrdp-blocking/Cargo.toml index 3ce5774e..557640c8 100644 --- a/crates/ironrdp-blocking/Cargo.toml +++ b/crates/ironrdp-blocking/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-blocking" -version = "0.7.0" +version = "0.8.0" readme = "README.md" description = "Blocking I/O abstraction wrapping the IronRDP state machines conveniently" edition.workspace = true @@ -16,7 +16,7 @@ doctest = false test = false [dependencies] -ironrdp-connector = { path = "../ironrdp-connector", version = "0.7" } # public +ironrdp-connector = { path = "../ironrdp-connector", version = "0.8" } # public ironrdp-core = { path = "../ironrdp-core", version = "0.1", features = ["alloc"] } # public ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6" } # public tracing = { version = "0.1", features = ["log"] } diff --git a/crates/ironrdp-client/Cargo.toml b/crates/ironrdp-client/Cargo.toml index 6cff8a9b..5a684444 100644 --- a/crates/ironrdp-client/Cargo.toml +++ b/crates/ironrdp-client/Cargo.toml @@ -32,7 +32,7 @@ qoiz = ["ironrdp/qoiz"] [dependencies] # Protocols -ironrdp = { path = "../ironrdp", version = "0.13", features = [ +ironrdp = { path = "../ironrdp", version = "0.14", features = [ "session", "input", "graphics", @@ -45,11 +45,11 @@ ironrdp = { path = "../ironrdp", version = "0.13", features = [ "connector", ] } ironrdp-core = { path = "../ironrdp-core", version = "0.1", features = ["alloc"] } -ironrdp-cliprdr-native = { path = "../ironrdp-cliprdr-native", version = "0.4" } +ironrdp-cliprdr-native = { path = "../ironrdp-cliprdr-native", version = "0.5" } ironrdp-rdpsnd-native = { path = "../ironrdp-rdpsnd-native", version = "0.4" } -ironrdp-tls = { path = "../ironrdp-tls", version = "0.1" } +ironrdp-tls = { path = "../ironrdp-tls", version = "0.2" } ironrdp-mstsgu = { path = "../ironrdp-mstsgu" } -ironrdp-tokio = { path = "../ironrdp-tokio", version = "0.7", features = ["reqwest"] } +ironrdp-tokio = { path = "../ironrdp-tokio", version = "0.8", features = ["reqwest"] } ironrdp-rdcleanpath.path = "../ironrdp-rdcleanpath" ironrdp-dvc-pipe-proxy.path = "../ironrdp-dvc-pipe-proxy" ironrdp-propertyset.path = "../ironrdp-propertyset" diff --git a/crates/ironrdp-cliprdr-native/CHANGELOG.md b/crates/ironrdp-cliprdr-native/CHANGELOG.md index 27b65067..20bbaa96 100644 --- a/crates/ironrdp-cliprdr-native/CHANGELOG.md +++ b/crates/ironrdp-cliprdr-native/CHANGELOG.md @@ -6,6 +6,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.5.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-cliprdr-native-v0.4.0...ironrdp-cliprdr-native-v0.5.0)] - 2025-12-18 + +### Bug Fixes + +- Prevent window class registration error on multiple sessions ([#1047](https://github.com/Devolutions/IronRDP/issues/1047)) ([a2af587e60](https://github.com/Devolutions/IronRDP/commit/a2af587e60e869f0235703e21772d1fc6a7dadcd)) + + When starting a second clipboard session, `RegisterClassA` would fail + with `ERROR_CLASS_ALREADY_EXISTS` because window classes are global to + the process. Now checks if the class is already registered before + attempting registration, allowing multiple WinClipboard instances to + coexist. + +### Build + +- Bump windows from 0.61.3 to 0.62.1 ([#1010](https://github.com/Devolutions/IronRDP/issues/1010)) ([79e71c4f90](https://github.com/Devolutions/IronRDP/commit/79e71c4f90ea68b14fe45241c1cf3953027b22a2)) + ## [[0.4.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-cliprdr-native-v0.3.0...ironrdp-cliprdr-native-v0.4.0)] - 2025-08-29 ### Bug Fixes diff --git a/crates/ironrdp-cliprdr-native/Cargo.toml b/crates/ironrdp-cliprdr-native/Cargo.toml index bf4e5a20..be193e66 100644 --- a/crates/ironrdp-cliprdr-native/Cargo.toml +++ b/crates/ironrdp-cliprdr-native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-cliprdr-native" -version = "0.4.0" +version = "0.5.0" readme = "README.md" description = "Native CLIPRDR static channel backend implementations for IronRDP" edition.workspace = true @@ -16,7 +16,7 @@ doctest = false test = false [dependencies] -ironrdp-cliprdr = { path = "../ironrdp-cliprdr", version = "0.4" } # public +ironrdp-cliprdr = { path = "../ironrdp-cliprdr", version = "0.5" } # public ironrdp-core = { path = "../ironrdp-core", version = "0.1" } tracing = { version = "0.1", features = ["log"] } diff --git a/crates/ironrdp-cliprdr/CHANGELOG.md b/crates/ironrdp-cliprdr/CHANGELOG.md index d96b4760..c9653f15 100644 --- a/crates/ironrdp-cliprdr/CHANGELOG.md +++ b/crates/ironrdp-cliprdr/CHANGELOG.md @@ -6,6 +6,30 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.5.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-cliprdr-v0.4.0...ironrdp-cliprdr-v0.5.0)] - 2025-12-18 + +### Bug Fixes + +- Fixes the Cliprdr `SvcProcessor` impl to support handling a `TemporaryDirectory` Clipboard PDU ([#1031](https://github.com/Devolutions/IronRDP/issues/1031)) ([f2326ef046](https://github.com/Devolutions/IronRDP/commit/f2326ef046cc81fb0e8985f03382859085882e86)) + +- Allow servers to announce clipboard ownership ([#1053](https://github.com/Devolutions/IronRDP/issues/1053)) ([d587b0c4c1](https://github.com/Devolutions/IronRDP/commit/d587b0c4c114c49d30f52859f43b22f829456a01)) + + Servers can now send Format List PDU via initiate_copy() regardless of + internal state. The existing state machine was designed for clients + where clipboard initialization must complete before announcing + ownership. + + MS-RDPECLIP Section 2.2.3.1 specifies that Format List PDU is sent by + either client or server when the local clipboard is updated. Servers + should be able to announce clipboard changes immediately after channel + negotiation. + + This change enables RDP servers to properly announce clipboard ownership + by bypassing the Initialization/Ready state check when R::is_server() is + true. Client behavior remains unchanged. + +- [**breaking**] Removed the `PackedMetafile::data()` method in favor of making the `PackedMetafile::data` field public. + ## [[0.4.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-cliprdr-v0.3.0...ironrdp-cliprdr-v0.4.0)] - 2025-08-29 ### Bug Fixes diff --git a/crates/ironrdp-cliprdr/Cargo.toml b/crates/ironrdp-cliprdr/Cargo.toml index e02cfefd..3dd216da 100644 --- a/crates/ironrdp-cliprdr/Cargo.toml +++ b/crates/ironrdp-cliprdr/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-cliprdr" -version = "0.4.0" +version = "0.5.0" readme = "README.md" description = "CLIPRDR static channel for clipboard implemented as described in MS-RDPECLIP" edition.workspace = true diff --git a/crates/ironrdp-connector/CHANGELOG.md b/crates/ironrdp-connector/CHANGELOG.md index 6fe86b40..888acabe 100644 --- a/crates/ironrdp-connector/CHANGELOG.md +++ b/crates/ironrdp-connector/CHANGELOG.md @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.8.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-connector-v0.7.1...ironrdp-connector-v0.8.0)] - 2025-12-18 + +### Build + +- Bump picky and sspi ([#1028](https://github.com/Devolutions/IronRDP/issues/1028)) ([5bd319126d](https://github.com/Devolutions/IronRDP/commit/5bd319126d32fbd8e505508e27ab2b1a18a83d04)) + + This fixes build issues with some dependencies. + ## [[0.7.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-connector-v0.7.0...ironrdp-connector-v0.7.1)] - 2025-09-04 ### Features diff --git a/crates/ironrdp-connector/Cargo.toml b/crates/ironrdp-connector/Cargo.toml index 45c22929..8119bab4 100644 --- a/crates/ironrdp-connector/Cargo.toml +++ b/crates/ironrdp-connector/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-connector" -version = "0.7.1" +version = "0.8.0" readme = "README.md" description = "State machines to drive an RDP connection sequence" edition.workspace = true diff --git a/crates/ironrdp-futures/CHANGELOG.md b/crates/ironrdp-futures/CHANGELOG.md index d0173f76..3a35de72 100644 --- a/crates/ironrdp-futures/CHANGELOG.md +++ b/crates/ironrdp-futures/CHANGELOG.md @@ -6,13 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.6.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-futures-v0.5.0...ironrdp-futures-v0.6.0)] - 2025-12-18 + + ## [[0.1.3](https://github.com/Devolutions/IronRDP/compare/ironrdp-futures-v0.1.2...ironrdp-futures-v0.1.3)] - 2025-03-12 ### Build - Update dependencies (#695) ([c21fa44fd6](https://github.com/Devolutions/IronRDP/commit/c21fa44fd6f3c6a6b74788ff68e83133c1314caa)) - ## [[0.1.2](https://github.com/Devolutions/IronRDP/compare/ironrdp-futures-v0.1.1...ironrdp-futures-v0.1.2)] - 2025-01-28 ### Documentation diff --git a/crates/ironrdp-futures/Cargo.toml b/crates/ironrdp-futures/Cargo.toml index aa8e67ec..ed75e138 100644 --- a/crates/ironrdp-futures/Cargo.toml +++ b/crates/ironrdp-futures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-futures" -version = "0.5.0" +version = "0.6.0" readme = "README.md" description = "`Framed*` traits implementation above futures’s traits" edition.workspace = true @@ -17,7 +17,7 @@ test = false [dependencies] futures-util = { version = "0.3", features = ["io"] } # public -ironrdp-async = { path = "../ironrdp-async", version = "0.7" } # public +ironrdp-async = { path = "../ironrdp-async", version = "0.8" } # public bytes = "1" # public [lints] diff --git a/crates/ironrdp-graphics/CHANGELOG.md b/crates/ironrdp-graphics/CHANGELOG.md index 41609a94..1a7bfd3c 100644 --- a/crates/ironrdp-graphics/CHANGELOG.md +++ b/crates/ironrdp-graphics/CHANGELOG.md @@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.7.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-graphics-v0.6.0...ironrdp-graphics-v0.7.0)] - 2025-12-18 + +### Added + +- [**breaking**] `InvalidIntegralConversion` variant in `RlgrError` and `ZgfxError` + +### Build + +- Bump bytemuck from 1.23.2 to 1.24.0 ([#1008](https://github.com/Devolutions/IronRDP/issues/1008)) ([a24a1fa9e8](https://github.com/Devolutions/IronRDP/commit/a24a1fa9e8f1898b2fcdd41d87660ab9e38f89ed)) + ## [[0.6.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-graphics-v0.5.0...ironrdp-graphics-v0.6.0)] - 2025-06-27 ### Bug Fixes diff --git a/crates/ironrdp-graphics/Cargo.toml b/crates/ironrdp-graphics/Cargo.toml index 0f2f4f2c..938612c1 100644 --- a/crates/ironrdp-graphics/Cargo.toml +++ b/crates/ironrdp-graphics/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-graphics" -version = "0.6.0" +version = "0.7.0" readme = "README.md" description = "RDP image processing primitives" edition.workspace = true diff --git a/crates/ironrdp-mstsgu/Cargo.toml b/crates/ironrdp-mstsgu/Cargo.toml index d5750df0..b194f467 100644 --- a/crates/ironrdp-mstsgu/Cargo.toml +++ b/crates/ironrdp-mstsgu/Cargo.toml @@ -30,7 +30,7 @@ hyper-util = { version = "0.1", features = ["tokio"] } hyper = { version = "1.7", features = ["client", "http1"] } ironrdp-core = { path = "../ironrdp-core", version = "0.1", features = ["std"] } ironrdp-error = { path = "../ironrdp-error", version = "0.1" } -ironrdp-tls = { path = "../ironrdp-tls", version = "0.1" } +ironrdp-tls = { path = "../ironrdp-tls", version = "0.2" } log = "0.4" tokio-tungstenite = { version = "0.28" } tokio-util = { version = "0.7" } diff --git a/crates/ironrdp-rdpdr-native/CHANGELOG.md b/crates/ironrdp-rdpdr-native/CHANGELOG.md index 4370951c..301fa404 100644 --- a/crates/ironrdp-rdpdr-native/CHANGELOG.md +++ b/crates/ironrdp-rdpdr-native/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.5.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpdr-native-v0.4.0...ironrdp-rdpdr-native-v0.5.0)] - 2025-12-18 + + ## [[0.4.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpdr-native-v0.3.0...ironrdp-rdpdr-native-v0.4.0)] - 2025-08-29 ### Build diff --git a/crates/ironrdp-rdpdr-native/Cargo.toml b/crates/ironrdp-rdpdr-native/Cargo.toml index 1d61a701..14fea584 100644 --- a/crates/ironrdp-rdpdr-native/Cargo.toml +++ b/crates/ironrdp-rdpdr-native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-rdpdr-native" -version = "0.4.0" +version = "0.5.0" readme = "README.md" description = "Native RDPDR static channel backend implementations for IronRDP" edition.workspace = true @@ -19,6 +19,6 @@ test = false ironrdp-core = { path = "../ironrdp-core", version = "0.1" } ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6" } # public ironrdp-svc = { path = "../ironrdp-svc", version = "0.5" } # public -ironrdp-rdpdr = { path = "../ironrdp-rdpdr", version = "0.4" } # public +ironrdp-rdpdr = { path = "../ironrdp-rdpdr", version = "0.5" } # public nix = { version = "0.30", features = ["fs", "dir"] } tracing = { version = "0.1", features = ["log"] } diff --git a/crates/ironrdp-rdpdr/CHANGELOG.md b/crates/ironrdp-rdpdr/CHANGELOG.md index 99da423d..e61a8251 100644 --- a/crates/ironrdp-rdpdr/CHANGELOG.md +++ b/crates/ironrdp-rdpdr/CHANGELOG.md @@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.5.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpdr-v0.4.1...ironrdp-rdpdr-v0.5.0)] - 2025-12-18 + +### Bug Fixes + +- Fix incorrect padding when parsing NDR strings ([#1015](https://github.com/Devolutions/IronRDP/issues/1015)) ([a0a3e750c9](https://github.com/Devolutions/IronRDP/commit/a0a3e750c9e4ee9c73b957fbcb26dbc59e57d07d)) + + When parsing Network Data Representation (NDR) messages, we're supposed + to account for padding at the end of strings to remain aligned on a + 4-byte boundary. The existing code doesn't seem to cover all cases, and + the resulting misalignment causes misleading errors when processing the + rest of the message. + ## [[0.4.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpdr-v0.4.0...ironrdp-rdpdr-v0.4.1)] - 2025-09-04 ### Features @@ -18,29 +30,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add USER_LOGGEDON flag support ([5e78f91713](https://github.com/Devolutions/IronRDP/commit/5e78f917132a174bdd5d8711beb1744de1bd265a)) - ## [[0.2.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpdr-v0.1.3...ironrdp-rdpdr-v0.2.0)] - 2025-03-12 ### Build - Bump ironrdp-pdu - ## [[0.1.3](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpdr-v0.1.2...ironrdp-rdpdr-v0.1.3)] - 2025-03-12 ### Build - Update dependencies (#695) ([c21fa44fd6](https://github.com/Devolutions/IronRDP/commit/c21fa44fd6f3c6a6b74788ff68e83133c1314caa)) - ## [[0.1.2](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpdr-v0.1.1...ironrdp-rdpdr-v0.1.2)] - 2025-01-28 ### Documentation - Use CDN URLs instead of the blob storage URLs for Devolutions logo (#631) ([dd249909a8](https://github.com/Devolutions/IronRDP/commit/dd249909a894004d4f728d30b3a4aa77a0f8193b)) - - ## [[0.1.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpdr-v0.1.0...ironrdp-rdpdr-v0.1.1)] - 2024-12-14 ### Other diff --git a/crates/ironrdp-rdpdr/Cargo.toml b/crates/ironrdp-rdpdr/Cargo.toml index 32a762e7..b585bff1 100644 --- a/crates/ironrdp-rdpdr/Cargo.toml +++ b/crates/ironrdp-rdpdr/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-rdpdr" -version = "0.4.1" +version = "0.5.0" readme = "README.md" description = "RDPDR channel implementation." edition.workspace = true diff --git a/crates/ironrdp-rdpsnd-native/CHANGELOG.md b/crates/ironrdp-rdpsnd-native/CHANGELOG.md index 9b3c6938..ff009f99 100644 --- a/crates/ironrdp-rdpsnd-native/CHANGELOG.md +++ b/crates/ironrdp-rdpsnd-native/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.4.2](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpsnd-native-v0.4.1...ironrdp-rdpsnd-native-v0.4.2)] - 2025-12-18 + +### Build + +- Bump bytemuck from 1.23.2 to 1.24.0 ([#1008](https://github.com/Devolutions/IronRDP/issues/1008)) ([a24a1fa9e8](https://github.com/Devolutions/IronRDP/commit/a24a1fa9e8f1898b2fcdd41d87660ab9e38f89ed)) + ## [[0.4.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpsnd-native-v0.4.0...ironrdp-rdpsnd-native-v0.4.1)] - 2025-09-24 ### Build @@ -49,7 +55,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use CDN URLs instead of the blob storage URLs for Devolutions logo (#631) ([dd249909a8](https://github.com/Devolutions/IronRDP/commit/dd249909a894004d4f728d30b3a4aa77a0f8193b)) - ## [[0.1.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpsnd-native-v0.1.0...ironrdp-rdpsnd-native-v0.1.1)] - 2024-12-15 ### Other diff --git a/crates/ironrdp-rdpsnd-native/Cargo.toml b/crates/ironrdp-rdpsnd-native/Cargo.toml index d99070a6..c6711eb0 100644 --- a/crates/ironrdp-rdpsnd-native/Cargo.toml +++ b/crates/ironrdp-rdpsnd-native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-rdpsnd-native" -version = "0.4.1" +version = "0.4.2" description = "Native RDPSND static channel backend implementations for IronRDP" edition.workspace = true license.workspace = true diff --git a/crates/ironrdp-server/CHANGELOG.md b/crates/ironrdp-server/CHANGELOG.md index e68c17d2..916740d6 100644 --- a/crates/ironrdp-server/CHANGELOG.md +++ b/crates/ironrdp-server/CHANGELOG.md @@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.10.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-server-v0.9.0...ironrdp-server-v0.10.0)] - 2025-12-18 + +### Bug Fixes + +- Send TLS close_notify during graceful RDP disconnect ([#1032](https://github.com/Devolutions/IronRDP/issues/1032)) ([a70e01d9c5](https://github.com/Devolutions/IronRDP/commit/a70e01d9c5675a7dffd65eda7428537c8ad6a857)) + + Add support for sending a proper TLS close_notify message when the RDP + client initiates a graceful disconnect PDU. + ## [[0.9.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-server-v0.8.0...ironrdp-server-v0.9.0)] - 2025-09-24 ### Bug Fixes diff --git a/crates/ironrdp-server/Cargo.toml b/crates/ironrdp-server/Cargo.toml index e4d531d3..b1e4650c 100644 --- a/crates/ironrdp-server/Cargo.toml +++ b/crates/ironrdp-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-server" -version = "0.9.0" +version = "0.10.0" readme = "README.md" description = "Extendable skeleton for implementing custom RDP servers" edition.workspace = true @@ -31,17 +31,17 @@ anyhow = "1.0" tokio = { version = "1", features = ["net", "macros", "sync", "rt"] } # public tokio-rustls = "0.26" # public async-trait = "0.1" -ironrdp-async = { path = "../ironrdp-async", version = "0.7" } +ironrdp-async = { path = "../ironrdp-async", version = "0.8" } ironrdp-ainput = { path = "../ironrdp-ainput", version = "0.4" } ironrdp-core = { path = "../ironrdp-core", version = "0.1" } ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6" } # public ironrdp-svc = { path = "../ironrdp-svc", version = "0.5" } # public -ironrdp-cliprdr = { path = "../ironrdp-cliprdr", version = "0.4" } # public +ironrdp-cliprdr = { path = "../ironrdp-cliprdr", version = "0.5" } # public ironrdp-displaycontrol = { path = "../ironrdp-displaycontrol", version = "0.4" } # public ironrdp-dvc = { path = "../ironrdp-dvc", version = "0.4" } # public -ironrdp-tokio = { path = "../ironrdp-tokio", version = "0.7" } -ironrdp-acceptor = { path = "../ironrdp-acceptor", version = "0.7" } # public -ironrdp-graphics = { path = "../ironrdp-graphics", version = "0.6" } # public +ironrdp-tokio = { path = "../ironrdp-tokio", version = "0.8" } +ironrdp-acceptor = { path = "../ironrdp-acceptor", version = "0.8" } # public +ironrdp-graphics = { path = "../ironrdp-graphics", version = "0.7" } # public ironrdp-rdpsnd = { path = "../ironrdp-rdpsnd", version = "0.6" } # public tracing = { version = "0.1", features = ["log"] } x509-cert = { version = "0.2.5", optional = true } diff --git a/crates/ironrdp-session/CHANGELOG.md b/crates/ironrdp-session/CHANGELOG.md index 3b043327..90011784 100644 --- a/crates/ironrdp-session/CHANGELOG.md +++ b/crates/ironrdp-session/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.8.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-session-v0.7.0...ironrdp-session-v0.8.0)] - 2025-12-18 + + ## [[0.6.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-session-v0.5.0...ironrdp-session-v0.6.0)] - 2025-08-29 ### Features diff --git a/crates/ironrdp-session/Cargo.toml b/crates/ironrdp-session/Cargo.toml index 1d9436aa..dd00422d 100644 --- a/crates/ironrdp-session/Cargo.toml +++ b/crates/ironrdp-session/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-session" -version = "0.7.0" +version = "0.8.0" readme = "README.md" description = "State machines to drive an RDP session" edition.workspace = true @@ -22,11 +22,11 @@ qoiz = ["dep:zstd-safe", "qoi"] [dependencies] ironrdp-core = { path = "../ironrdp-core", version = "0.1" } # public -ironrdp-connector = { path = "../ironrdp-connector", version = "0.7" } # public # TODO: at some point, this dependency could be removed (good for compilation speed) +ironrdp-connector = { path = "../ironrdp-connector", version = "0.8" } # public # TODO: at some point, this dependency could be removed (good for compilation speed) ironrdp-svc = { path = "../ironrdp-svc", version = "0.5" } # public ironrdp-dvc = { path = "../ironrdp-dvc", version = "0.4" } # public ironrdp-error = { path = "../ironrdp-error", version = "0.1" } # public -ironrdp-graphics = { path = "../ironrdp-graphics", version = "0.6" } # public +ironrdp-graphics = { path = "../ironrdp-graphics", version = "0.7" } # public ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6", features = ["std"] } # public ironrdp-displaycontrol = { path = "../ironrdp-displaycontrol", version = "0.4" } tracing = { version = "0.1", features = ["log"] } diff --git a/crates/ironrdp-tls/CHANGELOG.md b/crates/ironrdp-tls/CHANGELOG.md index 455742e7..2aaa1c25 100644 --- a/crates/ironrdp-tls/CHANGELOG.md +++ b/crates/ironrdp-tls/CHANGELOG.md @@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.2.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-tls-v0.1.4...ironrdp-tls-v0.2.0)] - 2025-12-18 + +### Features + +- [**breaking**] Return x509_cert::Certificate from upgrade() ([#1054](https://github.com/Devolutions/IronRDP/issues/1054)) ([bd2aed7686](https://github.com/Devolutions/IronRDP/commit/bd2aed76867f4038c32df9a0d24532ee40d2f14c)) + + This allows client applications to verify details of the certificate, + possibly with the user, when connecting to a server using TLS. + ## [[0.1.4](https://github.com/Devolutions/IronRDP/compare/ironrdp-tls-v0.1.3...ironrdp-tls-v0.1.4)] - 2025-08-29 ### Build diff --git a/crates/ironrdp-tls/Cargo.toml b/crates/ironrdp-tls/Cargo.toml index 1bfdf571..1c719be0 100644 --- a/crates/ironrdp-tls/Cargo.toml +++ b/crates/ironrdp-tls/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-tls" -version = "0.1.4" +version = "0.2.0" readme = "README.md" description = "TLS boilerplate common with most IronRDP clients" edition.workspace = true diff --git a/crates/ironrdp-tokio/CHANGELOG.md b/crates/ironrdp-tokio/CHANGELOG.md index 4265c72b..db3bd881 100644 --- a/crates/ironrdp-tokio/CHANGELOG.md +++ b/crates/ironrdp-tokio/CHANGELOG.md @@ -6,6 +6,49 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.8.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-tokio-v0.7.0...ironrdp-tokio-v0.8.0)] - 2025-12-18 + +### Features + +- Add MovableTokioFramed for Send+!Sync context ([#1033](https://github.com/Devolutions/IronRDP/issues/1033)) ([966ba8a53e](https://github.com/Devolutions/IronRDP/commit/966ba8a53e43a193271f40b9db80e45e495e2f24)) + + The `ironrdp-tokio` crate currently provides the following two + `Framed` implementations using the standard `tokio::io` traits: + - `type TokioFramed = Framed>` where `S: Send + Sync + + Unpin` + - `type LocalTokioFramed = Framed>` where `S: + Unpin` + + The former is meant for multi-threaded runtimes and the latter is meant + for single-threaded runtimes. + + This PR adds a third `Framed` implementation: + + `pub type MovableTokioFramed = Framed>` where + `S: Send + Unpin` + + This is a valid usecase as some implementations of the `tokio::io` + traits are `Send` but `!Sync`. Without this new third type, consumers of + `Framed` who have a `S: Send + !Sync` trait for their streams are + forced to downgrade to `LocalTokioFramed` and do some hacky workaround + with `tokio::task::spawn_blocking` since the defined associated futures, + `ReadFut` and `WriteAllFut`, are neither `Send` nor `Sync`. + +### Bug Fixes + +- [**breaking**] Use static dispatch for NetworkClient trait ([#1043](https://github.com/Devolutions/IronRDP/issues/1043)) ([bca6d190a8](https://github.com/Devolutions/IronRDP/commit/bca6d190a870708468534d224ff225a658767a9a)) + + - Rename `AsyncNetworkClient` to `NetworkClient` + - Replace dynamic dispatch (`Option<&mut dyn ...>`) with static dispatch + using generics (`&mut N where N: NetworkClient`) + - Reorder `connect_finalize` parameters for consistency across crates + +### Build + +- Bump picky and sspi ([#1028](https://github.com/Devolutions/IronRDP/issues/1028)) ([5bd319126d](https://github.com/Devolutions/IronRDP/commit/5bd319126d32fbd8e505508e27ab2b1a18a83d04)) + + This fixes build issues with some dependencies. + ## [[0.6.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-tokio-v0.5.1...ironrdp-tokio-v0.6.0)] - 2025-07-08 ### Build diff --git a/crates/ironrdp-tokio/Cargo.toml b/crates/ironrdp-tokio/Cargo.toml index dee2d0bd..abb08162 100644 --- a/crates/ironrdp-tokio/Cargo.toml +++ b/crates/ironrdp-tokio/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-tokio" -version = "0.7.0" +version = "0.8.0" readme = "README.md" description = "`Framed*` traits implementation above Tokio’s traits" edition.workspace = true @@ -23,8 +23,8 @@ reqwest-native-tls = ["reqwest", "reqwest?/native-tls"] [dependencies] bytes = "1" -ironrdp-async = { path = "../ironrdp-async", version = "0.7" } # public -ironrdp-connector = { path = "../ironrdp-connector", version = "0.7", optional = true } +ironrdp-async = { path = "../ironrdp-async", version = "0.8" } # public +ironrdp-connector = { path = "../ironrdp-connector", version = "0.8", optional = true } tokio = { version = "1", features = ["io-util"] } reqwest = { version = "0.12", default-features = false, features = ["http2", "system-proxy"], optional = true } sspi = { version = "0.18", features = ["network_client", "dns_resolver"], optional = true } diff --git a/crates/ironrdp/CHANGELOG.md b/crates/ironrdp/CHANGELOG.md index 848a10d0..48a74967 100644 --- a/crates/ironrdp/CHANGELOG.md +++ b/crates/ironrdp/CHANGELOG.md @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.14.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-v0.13.0...ironrdp-v0.14.0)] - 2025-12-18 + +### Build + +- Bump picky and sspi ([#1028](https://github.com/Devolutions/IronRDP/issues/1028)) ([5bd319126d](https://github.com/Devolutions/IronRDP/commit/5bd319126d32fbd8e505508e27ab2b1a18a83d04)) + + This fixes build issues with some dependencies. + ## [[0.13.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-v0.12.0...ironrdp-v0.13.0)] - 2025-09-24 ### Build diff --git a/crates/ironrdp/Cargo.toml b/crates/ironrdp/Cargo.toml index 21fc3331..25945735 100644 --- a/crates/ironrdp/Cargo.toml +++ b/crates/ironrdp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp" -version = "0.13.0" +version = "0.14.0" readme = "README.md" description = "A meta crate re-exporting IronRDP crates for convenience" edition.workspace = true @@ -40,22 +40,22 @@ __bench = ["ironrdp-server/__bench"] [dependencies] ironrdp-core = { path = "../ironrdp-core", version = "0.1", optional = true } # public ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6", optional = true } # public -ironrdp-cliprdr = { path = "../ironrdp-cliprdr", version = "0.4", optional = true } # public -ironrdp-connector = { path = "../ironrdp-connector", version = "0.7", optional = true } # public -ironrdp-acceptor = { path = "../ironrdp-acceptor", version = "0.7", optional = true } # public -ironrdp-session = { path = "../ironrdp-session", version = "0.7", optional = true } # public -ironrdp-graphics = { path = "../ironrdp-graphics", version = "0.6", optional = true } # public +ironrdp-cliprdr = { path = "../ironrdp-cliprdr", version = "0.5", optional = true } # public +ironrdp-connector = { path = "../ironrdp-connector", version = "0.8", optional = true } # public +ironrdp-acceptor = { path = "../ironrdp-acceptor", version = "0.8", optional = true } # public +ironrdp-session = { path = "../ironrdp-session", version = "0.8", optional = true } # public +ironrdp-graphics = { path = "../ironrdp-graphics", version = "0.7", optional = true } # public ironrdp-input = { path = "../ironrdp-input", version = "0.4", optional = true } # public -ironrdp-server = { path = "../ironrdp-server", version = "0.9", optional = true, features = ["helper"] } # public +ironrdp-server = { path = "../ironrdp-server", version = "0.10", optional = true, features = ["helper"] } # public ironrdp-svc = { path = "../ironrdp-svc", version = "0.5", optional = true } # public ironrdp-dvc = { path = "../ironrdp-dvc", version = "0.4", optional = true } # public -ironrdp-rdpdr = { path = "../ironrdp-rdpdr", version = "0.4", optional = true } # public +ironrdp-rdpdr = { path = "../ironrdp-rdpdr", version = "0.5", optional = true } # public ironrdp-rdpsnd = { path = "../ironrdp-rdpsnd", version = "0.6", optional = true } # public ironrdp-displaycontrol = { path = "../ironrdp-displaycontrol", version = "0.4", optional = true } # public [dev-dependencies] -ironrdp-blocking = { path = "../ironrdp-blocking", version = "0.7.0" } -ironrdp-cliprdr-native = { path = "../ironrdp-cliprdr-native", version = "0.4.0" } +ironrdp-blocking = { path = "../ironrdp-blocking", version = "0.8.0" } +ironrdp-cliprdr-native = { path = "../ironrdp-cliprdr-native", version = "0.5.0" } anyhow = "1" async-trait = "0.1" image = { version = "0.25.6", default-features = false, features = ["png"] } diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 8eac17ca..5d8e890a 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -63,9 +63,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" [[package]] name = "bit_field" @@ -75,9 +75,9 @@ checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" [[package]] name = "bitflags" -version = "2.9.4" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "bitvec" @@ -108,9 +108,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.2.39" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ "find-msvc-tools", "jobserver", @@ -120,9 +120,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "const-oid" @@ -150,9 +150,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -237,9 +237,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.2" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "flagset" @@ -249,9 +249,9 @@ checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" [[package]] name = "flate2" -version = "1.1.2" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "miniz_oxide", @@ -287,7 +287,7 @@ dependencies = [ [[package]] name = "ironrdp-cliprdr" -version = "0.4.0" +version = "0.5.0" dependencies = [ "bitflags", "ironrdp-core", @@ -363,7 +363,7 @@ dependencies = [ [[package]] name = "ironrdp-graphics" -version = "0.6.0" +version = "0.7.0" dependencies = [ "bit_field", "bitflags", @@ -400,7 +400,7 @@ dependencies = [ [[package]] name = "ironrdp-rdpdr" -version = "0.4.1" +version = "0.5.0" dependencies = [ "bitflags", "ironrdp-core", @@ -442,9 +442,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.176" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libfuzzer-sys" @@ -580,18 +580,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.41" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -636,9 +636,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "slab" @@ -658,9 +658,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.106" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -759,15 +759,15 @@ dependencies = [ [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "version_check"