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 diff --git a/Cargo.lock b/Cargo.lock index 1723af7d..980260bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -708,9 +708,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.48" +version = "4.5.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" +checksum = "0c2cfd7bf8a6017ddaa4e32ffe7403d547790db06bd171c1c53926faab501623" dependencies = [ "clap_builder", "clap_derive", @@ -718,9 +718,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.48" +version = "4.5.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" +checksum = "0a4c05b9e80c5ccd3a7ef080ad7b6ba7d6fc00a985b8b157197075677c82c7a0" 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", @@ -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", @@ -2597,7 +2599,6 @@ dependencies = [ "expect-test", "ironrdp-core", "ironrdp-pdu", - "lazy_static", "num-derive", "num-traits", "yuv", @@ -2643,7 +2644,6 @@ dependencies = [ "expect-test", "ironrdp-core", "ironrdp-error", - "lazy_static", "md-5", "num-bigint", "num-derive", @@ -2812,7 +2812,6 @@ dependencies = [ "ironrdp-rdpfile", "ironrdp-rdpsnd", "ironrdp-session", - "lazy_static", "paste", "png", "pretty_assertions", @@ -3712,9 +3711,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 +4439,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", diff --git a/Cargo.toml b/Cargo.toml index 2a6f5bb7..8332eaaa 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" @@ -94,7 +93,7 @@ ptr_cast_constness = "warn" # == Correctness == # #indexing_slicing = "warn" TODO: enable this lint project wide. -#as_conversions = "warn" +as_conversions = "warn" cast_lossless = "warn" cast_possible_truncation = "warn" cast_possible_wrap = "warn" @@ -129,6 +128,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/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-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/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 394050dd..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)) @@ -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-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-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..3018b1f7 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, @@ -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-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..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; @@ -282,12 +292,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 +332,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 +456,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 +469,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 +519,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 +532,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 +606,7 @@ impl From for u32 { #[derive(Debug, PartialEq)] pub struct ClosePdu { header: Header, - pub channel_id: DynamicChannelId, + channel_id: DynamicChannelId, } impl ClosePdu { @@ -583,6 +625,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)?; @@ -666,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(()) } @@ -689,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 } @@ -798,8 +848,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 +861,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-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/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/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/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/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 52231929..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; @@ -84,7 +86,7 @@ bitflags! { #[cfg(test)] mod test { - use lazy_static::lazy_static; + use std::sync::LazyLock; use super::*; @@ -111,29 +113,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..688e655d 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 _; @@ -78,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; @@ -134,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) } @@ -155,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(); @@ -178,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). @@ -191,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 }; @@ -223,8 +228,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 +432,8 @@ lazy_static::lazy_static! { distance_base: 17_094_304, }, }, - ]; -} + ] +}); #[derive(Debug)] pub enum ZgfxError { @@ -440,6 +445,7 @@ pub enum ZgfxError { uncompressed_size: usize, }, TokenBitsNotFound, + InvalidIntegralConversion(&'static str), } impl core::fmt::Display for ZgfxError { @@ -456,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"), } } } @@ -468,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 8ed962ad..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() }; @@ -350,7 +354,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 +373,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..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 = (); @@ -66,7 +76,7 @@ impl TryFrom for PktTy { #[derive(Default, Debug)] pub(crate) struct PktHdr { pub ty: PktTy, - _reserved: u16, + pub _reserved: u16, pub length: u32, } @@ -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); @@ -183,7 +193,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 { @@ -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(), }) } } @@ -384,13 +416,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 +491,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 +503,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 { @@ -489,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) } @@ -530,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/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/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/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/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/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/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/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/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-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_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/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/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-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-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/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) 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/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..f91d4719 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 { @@ -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"); @@ -222,6 +226,10 @@ impl DecodeStream { stream, }) } + + pub fn stream(&self) -> &Stream { + &self.stream + } } struct RxBuffer { 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/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-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/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/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/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-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/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..1ebe270e 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::{ @@ -41,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); @@ -125,43 +127,42 @@ 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 +}); +#[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( @@ -169,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( @@ -188,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( @@ -195,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( @@ -220,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/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..ae4a91a7 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 = u16::try_from(pdu.size()).unwrap(); + 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..87a65c8e 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,35 +36,36 @@ 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(), + }); +#[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(), @@ -72,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/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-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-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/crates/ironrdp-testsuite-extra/tests/tests/mod.rs b/crates/ironrdp-testsuite-extra/tests/mod.rs similarity index 99% rename from crates/ironrdp-testsuite-extra/tests/tests/mod.rs rename to crates/ironrdp-testsuite-extra/tests/mod.rs index 7127fa5f..8f5db6d6 100644 --- a/crates/ironrdp-testsuite-extra/tests/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-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 26d7eb76..83eab3f0 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; @@ -59,7 +60,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/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 357b3d40..38de6329 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. @@ -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); @@ -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/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/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/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 9312ba45..ceaa4e4e 100644 --- a/ffi/src/error.rs +++ b/ffi/src/error.rs @@ -6,12 +6,13 @@ 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; 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, @@ -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() + } + } +} diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index a6f3ad32..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]] @@ -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" @@ -782,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" 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) }