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..eb6350b0 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 + # net9.0-ios target packaged as Devolutions.IronRdp.iOS dotnet build .\ffi\dotnet\Devolutions.IronRdp\Devolutions.IronRdp.csproj -c Release /p:PackageId=Devolutions.IronRdp.iOS + shell: pwsh - name: Upload managed components uses: actions/upload-artifact@v4 @@ -349,12 +349,12 @@ 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 + permissions: + id-token: write steps: - name: Download NuGet package artifact @@ -363,19 +363,24 @@ jobs: name: ironrdp-nupkg path: package + - name: NuGet login (OIDC) + uses: NuGet/login@v1 + id: nuget-login + with: + user: ${{ secrets.NUGET_BOT_USERNAME }} + - name: Publish to nuget.org - 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 }}', + '${{ steps.nuget-login.outputs.NUGET_API_KEY }}', '--source', 'https://api.nuget.org/v3/index.json', '--skip-duplicate' @@ -385,19 +390,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..96d0a78d 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,15 @@ 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 + permissions: + id-token: write steps: - name: Checkout repository @@ -74,8 +75,12 @@ jobs: with: fetch-depth: 512 + - name: Authenticate with crates.io + id: auth + uses: rust-lang/crates-io-auth-action@v1 + - name: Run release-plz uses: Devolutions/actions-public/release-plz@v1 with: command: release - registry-token: ${{ secrets.CRATES_IO_TOKEN }} + registry-token: ${{ steps.auth.outputs.token }} diff --git a/Cargo.lock b/Cargo.lock index 78d07bb3..caaee2ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,15 +18,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" -[[package]] -name = "addr2line" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" -dependencies = [ - "gimli", -] - [[package]] name = "adler2" version = "2.0.1" @@ -35,19 +26,19 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aead" -version = "0.5.2" +version = "0.6.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +checksum = "ac8202ab55fcbf46ca829833f347a82a2a4ce0596f0304ac322c2d100030cd56" dependencies = [ - "crypto-common", - "generic-array", + "crypto-common 0.2.0-rc.4", + "inout", ] [[package]] name = "aes" -version = "0.8.4" +version = "0.9.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +checksum = "7e713c57c2a2b19159e7be83b9194600d7e8eb3b7c2cd67e671adf47ce189a05" dependencies = [ "cfg-if", "cipher", @@ -56,9 +47,9 @@ dependencies = [ [[package]] name = "aes-gcm" -version = "0.10.3" +version = "0.11.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +checksum = "0686ba04dc80c816104c96cd7782b748f6ad58c5dd4ee619ff3258cf68e83d54" dependencies = [ "aead", "aes", @@ -70,11 +61,12 @@ dependencies = [ [[package]] name = "aes-kw" -version = "0.2.1" +version = "0.3.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fa2b352dcefb5f7f3a5fb840e02665d311d878955380515e4fd50095dd3d8c" +checksum = "02eaa2d54d0fad0116e4b1efb65803ea0bf059ce970a67cd49718d87e807cb51" dependencies = [ "aes", + "const-oid 0.10.1", ] [[package]] @@ -84,7 +76,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", "version_check", "zerocopy", @@ -92,13 +84,22 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + [[package]] name = "alsa" version = "0.9.1" @@ -106,7 +107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43" dependencies = [ "alsa-sys", - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "libc", ] @@ -128,7 +129,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.9.4", + "bitflags 2.10.0", "cc", "cesu8", "jni", @@ -165,9 +166,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -195,22 +196,22 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.10" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -331,6 +332,15 @@ dependencies = [ "syn", ] +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -345,9 +355,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.14.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879b6c89592deb404ba4dc0ae6b58ffd1795c78991cbb5b8bc441c48a070440d" +checksum = "6a88aab2464f1f25453baa7a07c84c5b7684e274054ba06817f382357f77a288" dependencies = [ "aws-lc-sys", "zeroize", @@ -355,38 +365,21 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.32.2" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b715a6010afb9e457ca2b7c9d2b9c344baa8baed7b38dc476034c171b32575" +checksum = "b45afffdee1e7c9126814751f88dddc747f41d91da16c9551a0f1e8a11e788a1" dependencies = [ - "bindgen", "cc", "cmake", "dunce", "fs_extra", - "libloading", -] - -[[package]] -name = "backtrace" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-link 0.2.0", ] [[package]] name = "base16ct" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +checksum = "d8b59d472eab27ade8d770dcb11da7201c11234bef9f82ce7aa517be028d462b" [[package]] name = "base64" @@ -396,9 +389,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" [[package]] name = "benches" @@ -414,26 +407,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "bindgen" -version = "0.72.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" -dependencies = [ - "bitflags 2.9.4", - "cexpr", - "clang-sys", - "itertools", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn", -] - [[package]] name = "bit-set" version = "0.8.0" @@ -463,9 +436,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.4" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "bitvec" @@ -489,12 +462,21 @@ dependencies = [ ] [[package]] -name = "block-padding" -version = "0.3.3" +name = "block-buffer" +version = "0.11.0-rc.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +checksum = "e9ef36a6fcdb072aa548f3da057640ec10859eb4e91ddf526ee648d50c76a949" dependencies = [ - "generic-array", + "hybrid-array", +] + +[[package]] +name = "block-padding" +version = "0.4.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e59c1aab3e6c5e56afe1b7e8650be9b5a791cb997bdea449194ae62e4bf8c73" +dependencies = [ + "hybrid-array", ] [[package]] @@ -517,24 +499,24 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "bytemuck" -version = "1.23.2" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.10.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", @@ -555,15 +537,15 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "bytesize" -version = "2.1.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5c434ae3cf0089ca203e9019ebe529c47ff45cefe8af7c85ecb734ef541822f" +checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3" [[package]] name = "calloop" @@ -571,7 +553,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "log", "polling", "rustix 0.38.44", @@ -599,18 +581,18 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cbc" -version = "0.1.2" +version = "0.2.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +checksum = "5dbf9e5b071e9de872e32b73f485e8f644ff47c7011d95476733e7482ee3e5c3" dependencies = [ "cipher", ] [[package]] name = "cc" -version = "1.2.39" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ "find-msvc-tools", "jobserver", @@ -624,20 +606,11 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -655,7 +628,7 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-link 0.2.0", + "windows-link", ] [[package]] @@ -687,30 +660,20 @@ dependencies = [ [[package]] name = "cipher" -version = "0.4.4" +version = "0.5.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +checksum = "1e12a13eb01ded5d32ee9658d94f553a19e804204f2dc811df69ab4d9e0cb8c7" dependencies = [ - "crypto-common", + "block-buffer 0.11.0-rc.5", + "crypto-common 0.2.0-rc.4", "inout", ] -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" -version = "4.5.48" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" dependencies = [ "clap_builder", "clap_derive", @@ -718,9 +681,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.48" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" dependencies = [ "anstream", "anstyle", @@ -730,9 +693,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", @@ -742,15 +705,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "cmake" -version = "0.1.54" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" dependencies = [ "cc", ] @@ -797,10 +760,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] -name = "convert_case" -version = "0.7.1" +name = "const-oid" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +checksum = "0dabb6555f92fb9ee4140454eb5dcd14c7960e1225c6d1a6cc361f032947713e" + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" dependencies = [ "unicode-segmentation", ] @@ -839,20 +808,7 @@ checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", "core-foundation 0.9.4", - "core-graphics-types 0.1.3", - "foreign-types 0.5.0", - "libc", -] - -[[package]] -name = "core-graphics" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" -dependencies = [ - "bitflags 2.9.4", - "core-foundation 0.10.1", - "core-graphics-types 0.2.0", + "core-graphics-types", "foreign-types 0.5.0", "libc", ] @@ -868,17 +824,6 @@ dependencies = [ "libc", ] -[[package]] -name = "core-graphics-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" -dependencies = [ - "bitflags 2.9.4", - "core-foundation 0.10.1", - "libc", -] - [[package]] name = "coreaudio-rs" version = "0.13.0" @@ -939,10 +884,11 @@ dependencies = [ [[package]] name = "criterion" -version = "0.7.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1c047a62b0cc3e145fa84415a3191f628e980b194c2755aa12300a4e6cbd928" +checksum = "4d883447757bb0ee46f233e9dc22eb84d93a9508c9b868687b274fc431d886bf" dependencies = [ + "alloca", "anes", "cast", "ciborium", @@ -951,6 +897,7 @@ dependencies = [ "itertools", "num-traits", "oorandom", + "page_size", "plotters", "rayon", "regex", @@ -962,14 +909,29 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.6.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1bcc0dc7dfae599d84ad0b1a55f80cde8af3725da8313b528da95ef783e338" +checksum = "ed943f81ea2faa8dcecbbfa50164acf95d555afec96a27871663b300e387b2e4" dependencies = [ "cast", "itertools", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.6" @@ -1001,7 +963,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "crossterm_winapi", "derive_more", "document-features", @@ -1028,38 +990,40 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" -[[package]] -name = "crypto" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf1e6e5492f8f0830c37f301f6349e0dac8b2466e4fe89eef90e9eef906cd046" -dependencies = [ - "crypto-common", -] - [[package]] name = "crypto-bigint" -version = "0.5.5" +version = "0.7.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +checksum = "4113edbc9f68c0a64d5b911f803eb245d04bb812680fd56776411f69c670f3e0" dependencies = [ - "generic-array", - "rand_core 0.6.4", + "hybrid-array", + "num-traits", + "rand_core 0.9.3", + "serdect", "subtle", "zeroize", ] [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", - "rand_core 0.6.4", "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8235645834fbc6832939736ce2f2d08192652269e11010a6240f61b908a1c6" +dependencies = [ + "hybrid-array", + "rand_core 0.9.3", +] + [[package]] name = "crypto-mac" version = "0.11.0" @@ -1070,6 +1034,40 @@ dependencies = [ "subtle", ] +[[package]] +name = "crypto-primes" +version = "0.7.0-pre.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f2523fbb68811c8710829417ad488086720a6349e337c38d12fa81e09e50bf" +dependencies = [ + "crypto-bigint", + "libm", + "rand_core 0.9.3", +] + +[[package]] +name = "cryptoki" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "781357a7779a8e92ea985121bbf379a9adf0777f44ab6392efc6abd5aa9b67db" +dependencies = [ + "bitflags 1.3.2", + "cryptoki-sys", + "libloading", + "log", + "paste", + "secrecy", +] + +[[package]] +name = "cryptoki-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "753e27d860277930ae9f394c119c8c70303236aab0ffab1d51f3d207dbb2bc4b" +dependencies = [ + "libloading", +] + [[package]] name = "ctor-lite" version = "0.1.0" @@ -1078,9 +1076,9 @@ checksum = "1f791803201ab277ace03903de1594460708d2d54df6053f2d9e82f592b19e3b" [[package]] name = "ctr" -version = "0.9.2" +version = "0.10.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +checksum = "27e41d01c6f73b9330177f5cf782ae5b581b5f2c7840e298e0275ceee5001434" dependencies = [ "cipher", ] @@ -1093,14 +1091,14 @@ checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" [[package]] name = "curve25519-dalek" -version = "4.1.3" +version = "5.0.0-pre.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +checksum = "6f9200d1d13637f15a6acb71e758f64624048d85b31a5fdbfd8eca1e2687d0b7" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", - "digest", + "digest 0.11.0-rc.3", "fiat-crypto", "rustc_version", "subtle", @@ -1136,10 +1134,21 @@ version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ - "const-oid", + "const-oid 0.9.6", "der_derive", "flagset", - "pem-rfc7468", + "pem-rfc7468 0.7.0", + "zeroize", +] + +[[package]] +name = "der" +version = "0.8.0-rc.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d8dd2f26c86b27a2a8ea2767ec7f9df7a89516e4794e54ac01ee618dda3aa4" +dependencies = [ + "const-oid 0.10.1", + "pem-rfc7468 1.0.0-rc.3", "zeroize", ] @@ -1169,9 +1178,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", ] @@ -1189,30 +1198,31 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" dependencies = [ "convert_case", "proc-macro2", "quote", + "rustc_version", "syn", ] [[package]] name = "des" -version = "0.8.1" +version = "0.9.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdd80ce8ce993de27e9f063a444a4d53ce8e8db4c1f00cc03af5ad5a9867a1e" +checksum = "3f51594a70805988feb1c85495ddec0c2052e4fbe59d9c0bb7f94bfc164f4f90" dependencies = [ "cipher", ] @@ -1229,9 +1239,19 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", + "block-buffer 0.10.4", + "crypto-common 0.1.7", +] + +[[package]] +name = "digest" +version = "0.11.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac89f8a64533a9b0eaa73a68e424db0fb1fd6271c74cc0125336a05f090568d" +dependencies = [ + "block-buffer 0.11.0-rc.5", + "const-oid 0.10.1", + "crypto-common 0.2.0-rc.4", "subtle", ] @@ -1278,8 +1298,8 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.2", + "bitflags 2.10.0", + "objc2 0.6.3", ] [[package]] @@ -1310,9 +1330,9 @@ dependencies = [ [[package]] name = "document-features" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" dependencies = [ "litrs", ] @@ -1331,22 +1351,23 @@ checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] name = "drm" -version = "0.12.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1" +checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "bytemuck", "drm-ffi", "drm-fourcc", + "libc", "rustix 0.38.44", ] [[package]] name = "drm-ffi" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97c98727e48b7ccb4f4aea8cfe881e5b07f702d17b7875991881b41af7278d53" +checksum = "d8e41459d99a9b529845f6d2c909eb9adf3b6d2f82635ae40be8de0601726e8b" dependencies = [ "drm-sys", "rustix 0.38.44", @@ -1360,9 +1381,9 @@ checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" [[package]] name = "drm-sys" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd39dde40b6e196c2e8763f23d119ddb1a8714534bf7d77fa97a65b0feda3986" +checksum = "bafb66c8dbc944d69e15cfcc661df7e703beffbaec8bd63151368b06c5f9858c" dependencies = [ "libc", "linux-raw-sys 0.6.5", @@ -1382,23 +1403,24 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "ecdsa" -version = "0.16.9" +version = "0.17.0-rc.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +checksum = "b4ab355ec063f7a110eb627471058093aba00eb7f4e70afbd15e696b79d1077b" dependencies = [ - "der", - "digest", + "der 0.8.0-rc.9", + "digest 0.11.0-rc.3", "elliptic-curve", "rfc6979", "signature", - "spki", + "spki 0.8.0-rc.4", + "zeroize", ] [[package]] name = "ed25519" -version = "2.2.3" +version = "3.0.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +checksum = "9ef49c0b20c0ad088893ad2a790a29c06a012b3f05bcfc66661fd22a94b32129" dependencies = [ "pkcs8", "signature", @@ -1406,14 +1428,13 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.2.0" +version = "3.0.0-pre.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +checksum = "ad207ed88a133091f83224265eac21109930db09bedcad05d5252f2af2de20a1" dependencies = [ "curve25519-dalek", "ed25519", - "rand_core 0.6.4", - "serde", + "rand_core 0.9.3", "sha2", "subtle", "zeroize", @@ -1427,20 +1448,21 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "elliptic-curve" -version = "0.13.8" +version = "0.14.0-rc.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +checksum = "2e3be87c458d756141f3b6ee188828132743bf90c7d14843e2835d6443e5fb03" dependencies = [ "base16ct", "crypto-bigint", - "digest", + "digest 0.11.0-rc.3", "ff", - "generic-array", "group", "hkdf", - "pem-rfc7468", + "hybrid-array", + "once_cell", + "pem-rfc7468 1.0.0-rc.3", "pkcs8", - "rand_core 0.6.4", + "rand_core 0.9.3", "sec1", "subtle", "zeroize", @@ -1485,7 +1507,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -1515,11 +1537,11 @@ dependencies = [ [[package]] name = "ff" -version = "0.13.1" +version = "0.14.0-pre.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +checksum = "d42dd26f5790eda47c1a2158ea4120e32c35ddc9a7743c98a292accc01b54ef3" dependencies = [ - "rand_core 0.6.4", + "rand_core 0.9.3", "subtle", ] @@ -1527,6 +1549,7 @@ dependencies = [ name = "ffi" version = "0.0.0" dependencies = [ + "anyhow", "diplomat", "diplomat-runtime", "embed-resource", @@ -1534,6 +1557,7 @@ dependencies = [ "ironrdp-cliprdr-native", "ironrdp-core", "ironrdp-dvc-pipe-proxy", + "ironrdp-rdcleanpath", "sspi", "thiserror 2.0.17", "tracing", @@ -1542,15 +1566,15 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.9" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" [[package]] name = "find-msvc-tools" -version = "0.1.2" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "flagset" @@ -1560,11 +1584,12 @@ checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" [[package]] name = "flate2" -version = "1.1.2" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", + "libz-sys", "miniz_oxide", ] @@ -1749,17 +1774,16 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", - "zeroize", ] [[package]] name = "gethostname" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc257fdb4038301ce4b9cd1b3b51704509692bb3ff716a410cbd07925d9dae55" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ "rustix 1.1.2", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -1771,40 +1795,33 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi", - "wasi 0.14.7+wasi-0.2.4", + "wasip2", "wasm-bindgen", ] [[package]] name = "ghash" -version = "0.5.1" +version = "0.6.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +checksum = "4f88107cb02ed63adcc4282942e60c4d09d80208d33b360ce7c729ce6dae1739" dependencies = [ - "opaque-debug", "polyval", ] -[[package]] -name = "gimli" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" - [[package]] name = "glob" version = "0.3.3" @@ -1856,12 +1873,12 @@ dependencies = [ [[package]] name = "group" -version = "0.13.0" +version = "0.14.0-pre.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +checksum = "1ff6a0b2dd4b981b1ae9e3e6830ab146771f3660d31d57bafd9018805a91b0f1" dependencies = [ "ff", - "rand_core 0.6.4", + "rand_core 0.9.3", "subtle", ] @@ -1886,19 +1903,42 @@ dependencies = [ [[package]] name = "half" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", + "zerocopy", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", ] [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "spin", + "stable_deref_trait", +] [[package]] name = "heck" @@ -1920,9 +1960,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hickory-proto" -version = "0.24.4" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92652067c9ce6f66ce53cc38d1169daa36e6e7eb7dd3b63b5103bd9d97117248" +checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" dependencies = [ "async-trait", "cfg-if", @@ -1934,8 +1974,9 @@ dependencies = [ "idna", "ipnet", "once_cell", - "rand 0.8.5", - "thiserror 1.0.69", + "rand 0.9.2", + "ring", + "thiserror 2.0.17", "tinyvec", "tokio", "tracing", @@ -1944,51 +1985,50 @@ dependencies = [ [[package]] name = "hickory-resolver" -version = "0.24.4" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbb117a1ca520e111743ab2f6688eddee69db4e0ea242545a604dce8a66fd22e" +checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a" dependencies = [ "cfg-if", "futures-util", "hickory-proto", "ipconfig", - "lru-cache", + "moka", "once_cell", "parking_lot", - "rand 0.8.5", + "rand 0.9.2", "resolv-conf", "smallvec", - "thiserror 1.0.69", + "thiserror 2.0.17", "tokio", "tracing", ] [[package]] name = "hkdf" -version = "0.12.4" +version = "0.13.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +checksum = "d8ef30358b03ca095a5b910547f4f8d4b9f163e4057669c5233ef595b1ecf008" dependencies = [ "hmac", ] [[package]] name = "hmac" -version = "0.12.1" +version = "0.13.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +checksum = "a3fd4dc94c318c1ede8a2a48341c250d6ddecd3ba793da2820301a9f92417ad9" dependencies = [ - "digest", + "digest 0.11.0-rc.3", ] [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -2022,10 +2062,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] -name = "hyper" -version = "1.7.0" +name = "hybrid-array" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "f471e0a81b2f90ffc0cb2f951ae04da57de8baa46fa99112b062a5173a5088d0" +dependencies = [ + "subtle", + "typenum", + "zeroize", +] + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", @@ -2078,9 +2129,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.17" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ "base64", "bytes", @@ -2094,7 +2145,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.0", + "socket2 0.6.1", "system-configuration", "tokio", "tower-service", @@ -2114,7 +2165,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.1", + "windows-core 0.62.2", ] [[package]] @@ -2128,9 +2179,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -2141,9 +2192,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -2154,11 +2205,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -2169,42 +2219,38 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -2235,9 +2281,9 @@ dependencies = [ [[package]] name = "image" -version = "0.25.8" +version = "0.25.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "529feb3e6769d234375c4cf1ee2ce713682b8e76538cb13f9fc23e1400a591e7" +checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" dependencies = [ "bytemuck", "byteorder-lite", @@ -2248,9 +2294,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.4" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", "hashbrown", @@ -2258,12 +2304,12 @@ dependencies = [ [[package]] name = "inout" -version = "0.1.4" +version = "0.2.0-rc.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +checksum = "1603f76010ff924b616c8f44815a42eb10fb0b93d308b41deaa8da6d4251fd4b" dependencies = [ "block-padding", - "generic-array", + "hybrid-array", ] [[package]] @@ -2272,7 +2318,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2628910d0114e9139056161d8644a2026be7b117f8498943f9437748b04c9e0a" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "crossterm", "dyn-clone", "fuzzy-matcher", @@ -2280,17 +2326,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags 2.9.4", - "cfg-if", - "libc", -] - [[package]] name = "ipconfig" version = "0.3.2" @@ -2311,9 +2346,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" dependencies = [ "memchr", "serde", @@ -2333,7 +2368,7 @@ dependencies = [ [[package]] name = "ironrdp" -version = "0.13.0" +version = "0.14.0" dependencies = [ "anyhow", "async-trait", @@ -2366,7 +2401,7 @@ dependencies = [ [[package]] name = "ironrdp-acceptor" -version = "0.7.0" +version = "0.8.0" dependencies = [ "ironrdp-async", "ironrdp-connector", @@ -2380,7 +2415,7 @@ dependencies = [ name = "ironrdp-ainput" version = "0.4.0" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "ironrdp-core", "ironrdp-dvc", "num-derive", @@ -2389,7 +2424,7 @@ dependencies = [ [[package]] name = "ironrdp-async" -version = "0.7.0" +version = "0.8.0" dependencies = [ "bytes", "ironrdp-connector", @@ -2410,7 +2445,7 @@ dependencies = [ [[package]] name = "ironrdp-blocking" -version = "0.7.0" +version = "0.8.0" dependencies = [ "bytes", "ironrdp-connector", @@ -2461,16 +2496,16 @@ dependencies = [ "url", "uuid", "whoami", - "windows 0.61.3", + "windows 0.62.2", "winit", "x509-cert", ] [[package]] name = "ironrdp-cliprdr" -version = "0.4.0" +version = "0.5.0" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "ironrdp-core", "ironrdp-pdu", "ironrdp-svc", @@ -2487,17 +2522,17 @@ dependencies = [ [[package]] name = "ironrdp-cliprdr-native" -version = "0.4.0" +version = "0.5.0" dependencies = [ "ironrdp-cliprdr", "ironrdp-core", "tracing", - "windows 0.61.3", + "windows 0.62.2", ] [[package]] name = "ironrdp-connector" -version = "0.7.1" +version = "0.8.0" dependencies = [ "arbitrary", "ironrdp-core", @@ -2561,7 +2596,7 @@ version = "0.1.3" [[package]] name = "ironrdp-futures" -version = "0.5.0" +version = "0.6.0" dependencies = [ "bytes", "futures-util", @@ -2586,10 +2621,10 @@ dependencies = [ [[package]] name = "ironrdp-graphics" -version = "0.6.0" +version = "0.7.0" dependencies = [ "bit_field", - "bitflags 2.9.4", + "bitflags 2.10.0", "bitvec", "bmp", "bytemuck", @@ -2597,7 +2632,6 @@ dependencies = [ "expect-test", "ironrdp-core", "ironrdp-pdu", - "lazy_static", "num-derive", "num-traits", "yuv", @@ -2617,7 +2651,7 @@ name = "ironrdp-mstsgu" version = "0.0.1" dependencies = [ "base64", - "bitflags 2.9.4", + "bitflags 2.10.0", "futures-util", "http-body-util", "hyper", @@ -2637,20 +2671,19 @@ name = "ironrdp-pdu" version = "0.6.0" dependencies = [ "bit_field", - "bitflags 2.9.4", + "bitflags 2.10.0", "byteorder", "der-parser", "expect-test", "ironrdp-core", "ironrdp-error", - "lazy_static", - "md-5", + "md-5 0.10.6", "num-bigint", "num-derive", "num-integer", "num-traits", - "pkcs1", - "sha1", + "pkcs1 0.7.5", + "sha1 0.10.6", "tap", "thiserror 2.0.17", "x509-cert", @@ -2671,14 +2704,14 @@ dependencies = [ name = "ironrdp-rdcleanpath" version = "0.2.1" dependencies = [ - "der", + "der 0.7.10", ] [[package]] name = "ironrdp-rdpdr" -version = "0.4.1" +version = "0.5.0" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "ironrdp-core", "ironrdp-error", "ironrdp-pdu", @@ -2688,7 +2721,7 @@ dependencies = [ [[package]] name = "ironrdp-rdpdr-native" -version = "0.4.0" +version = "0.5.0" dependencies = [ "ironrdp-core", "ironrdp-pdu", @@ -2709,7 +2742,7 @@ dependencies = [ name = "ironrdp-rdpsnd" version = "0.6.0" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "ironrdp-core", "ironrdp-pdu", "ironrdp-svc", @@ -2718,7 +2751,7 @@ dependencies = [ [[package]] name = "ironrdp-rdpsnd-native" -version = "0.4.1" +version = "0.4.2" dependencies = [ "anyhow", "bytemuck", @@ -2731,7 +2764,7 @@ dependencies = [ [[package]] name = "ironrdp-server" -version = "0.9.0" +version = "0.10.0" dependencies = [ "anyhow", "async-trait", @@ -2761,7 +2794,7 @@ dependencies = [ [[package]] name = "ironrdp-session" -version = "0.7.0" +version = "0.8.0" dependencies = [ "ironrdp-connector", "ironrdp-core", @@ -2784,7 +2817,7 @@ version = "0.0.0" name = "ironrdp-svc" version = "0.5.0" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "ironrdp-core", "ironrdp-pdu", ] @@ -2812,7 +2845,6 @@ dependencies = [ "ironrdp-rdpfile", "ironrdp-rdpsnd", "ironrdp-session", - "lazy_static", "paste", "png", "pretty_assertions", @@ -2839,7 +2871,7 @@ dependencies = [ [[package]] name = "ironrdp-tls" -version = "0.1.4" +version = "0.2.0" dependencies = [ "tokio", "tokio-native-tls", @@ -2849,7 +2881,7 @@ dependencies = [ [[package]] name = "ironrdp-tokio" -version = "0.7.0" +version = "0.8.0" dependencies = [ "bytes", "ironrdp-async", @@ -2870,7 +2902,7 @@ dependencies = [ "futures-channel", "futures-util", "getrandom 0.2.16", - "getrandom 0.3.3", + "getrandom 0.3.4", "gloo-net", "gloo-timers", "iron-remote-desktop", @@ -2900,9 +2932,27 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "iso7816" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3c7e91da489667bb054f9cd2f1c60cc2ac4478a899f403d11dbc62189215b0" +dependencies = [ + "heapless", +] + +[[package]] +name = "iso7816-tlv" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7660d28d24a831d690228a275d544654a30f3b167a8e491cf31af5fe5058b546" +dependencies = [ + "untrusted", +] [[package]] name = "itertools" @@ -2947,15 +2997,15 @@ version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "libc", ] [[package]] name = "js-sys" -version = "0.3.81" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -2963,9 +3013,9 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.5" +version = "0.2.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +checksum = "3d546793a04a1d3049bd192856f804cfe96356e2cf36b54b4e575155babe9f41" dependencies = [ "cpufeatures", ] @@ -2975,24 +3025,21 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] [[package]] name = "libc" -version = "0.2.176" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libloading" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-targets 0.53.4", + "windows-link", ] [[package]] @@ -3014,20 +3061,25 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "libc", - "redox_syscall 0.5.17", + "redox_syscall 0.6.0", ] [[package]] -name = "linked-hash-map" -version = "0.5.6" +name = "libz-sys" +version = "1.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] [[package]] name = "linux-raw-sys" @@ -3049,40 +3101,30 @@ checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "litrs" -version = "0.4.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" - -[[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", -] +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "lru-slab" @@ -3115,7 +3157,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if", - "digest", + "digest 0.10.7", +] + +[[package]] +name = "md-5" +version = "0.11.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9ec86664728010f574d67ef01aec964e6f1299241a3402857c1a8a390a62478" +dependencies = [ + "cfg-if", + "digest 0.11.0-rc.3", ] [[package]] @@ -3124,7 +3176,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da5ac363534dce5fabf69949225e174fbf111a498bf0ff794c8ea1fba9f3dda" dependencies = [ - "digest", + "digest 0.10.7", ] [[package]] @@ -3135,9 +3187,9 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memmap2" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" +checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" dependencies = [ "libc", ] @@ -3160,21 +3212,39 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "log", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "moka" +version = "0.12.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8261cd88c312e0004c1d51baad2980c66528dfdb2bee62003e643a4d8f86b077" +dependencies = [ + "crossbeam-channel", + "crossbeam-epoch", + "crossbeam-utils", + "equivalent", + "parking_lot", + "portable-atomic", + "rustc_version", + "smallvec", + "tagptr", + "uuid", ] [[package]] name = "moxcms" -version = "0.7.5" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd32fa8935aeadb8a8a6b6b351e40225570a37c43de67690383d87ef170cd08" +checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" dependencies = [ "num-traits", "pxfm", @@ -3203,7 +3273,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "jni-sys", "log", "ndk-sys", @@ -3233,7 +3303,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "cfg_aliases", "libc", @@ -3251,11 +3321,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.50.1" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3268,24 +3338,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "serde", - "smallvec", - "zeroize", -] - [[package]] name = "num-conv" version = "0.1.0" @@ -3312,17 +3364,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -3330,14 +3371,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", - "libm", ] [[package]] name = "num_enum" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" dependencies = [ "num_enum_derive", "rustversion", @@ -3345,9 +3385,9 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -3373,9 +3413,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561f357ba7f3a2a61563a186a163d0a3a5247e1089524a3981d49adb775078bc" +checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" dependencies = [ "objc2-encode", ] @@ -3386,29 +3426,29 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "block2", "libc", "objc2 0.5.2", "objc2-core-data", "objc2-core-image", "objc2-foundation 0.2.2", - "objc2-quartz-core", + "objc2-quartz-core 0.2.2", ] [[package]] name = "objc2-audio-toolbox" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10cbe18d879e20a4aea544f8befe38bcf52255eb63d3f23eca2842f3319e4c07" +checksum = "6948501a91121d6399b79abaa33a8aa4ea7857fe019f341b8c23ad6e81b79b08" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "libc", - "objc2 0.6.2", + "objc2 0.6.3", "objc2-core-audio", "objc2-core-audio-types", "objc2-core-foundation", - "objc2-foundation 0.3.1", + "objc2-foundation 0.3.2", ] [[package]] @@ -3417,7 +3457,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "block2", "objc2 0.5.2", "objc2-core-location", @@ -3437,24 +3477,24 @@ dependencies = [ [[package]] name = "objc2-core-audio" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca44961e888e19313b808f23497073e3f6b3c22bb485056674c8b49f3b025c82" +checksum = "e1eebcea8b0dbff5f7c8504f3107c68fc061a3eb44932051c8cf8a68d969c3b2" dependencies = [ "dispatch2", - "objc2 0.6.2", + "objc2 0.6.3", "objc2-core-audio-types", "objc2-core-foundation", ] [[package]] name = "objc2-core-audio-types" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f1cc99bb07ad2ddb6527ddf83db6a15271bb036b3eb94b801cd44fdc666ee1" +checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c" dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.2", + "bitflags 2.10.0", + "objc2 0.6.3", ] [[package]] @@ -3463,7 +3503,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3471,13 +3511,26 @@ dependencies = [ [[package]] name = "objc2-core-foundation" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "dispatch2", - "objc2 0.6.2", + "objc2 0.6.3", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.10.0", + "dispatch2", + "objc2 0.6.3", + "objc2-core-foundation", + "objc2-io-surface", ] [[package]] @@ -3516,7 +3569,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "block2", "dispatch", "libc", @@ -3525,11 +3578,24 @@ dependencies = [ [[package]] name = "objc2-foundation" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "objc2 0.6.2", + "bitflags 2.10.0", + "objc2 0.6.3", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.10.0", + "objc2 0.6.3", + "objc2-core-foundation", ] [[package]] @@ -3550,7 +3616,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3562,13 +3628,25 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", "objc2-metal", ] +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.10.0", + "objc2 0.6.3", + "objc2-core-foundation", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-symbols" version = "0.2.2" @@ -3585,7 +3663,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "block2", "objc2 0.5.2", "objc2-cloud-kit", @@ -3594,7 +3672,7 @@ dependencies = [ "objc2-core-location", "objc2-foundation 0.2.2", "objc2-link-presentation", - "objc2-quartz-core", + "objc2-quartz-core 0.2.2", "objc2-symbols", "objc2-uniform-type-identifiers", "objc2-user-notifications", @@ -3617,22 +3695,13 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "block2", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", ] -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", -] - [[package]] name = "oid" version = "0.2.1" @@ -3647,12 +3716,16 @@ name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +dependencies = [ + "critical-section", + "portable-atomic", +] [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "oorandom" @@ -3660,19 +3733,13 @@ version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - [[package]] name = "openssl" -version = "0.10.73" +version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cfg-if", "foreign-types 0.3.2", "libc", @@ -3700,9 +3767,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.109" +version = "0.9.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", @@ -3712,18 +3779,18 @@ 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", ] [[package]] name = "orbclient" -version = "0.3.48" +version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba0b26cec2e24f08ed8bb31519a9333140a6599b867dac464bb150bdb796fd43" +checksum = "247ad146e19b9437f8604c21f8652423595cf710ad108af40e77d3ae6e96b827" dependencies = [ "libredox", ] @@ -3739,47 +3806,61 @@ dependencies = [ [[package]] name = "p256" -version = "0.13.2" +version = "0.14.0-pre.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +checksum = "81b374901df34ee468167a58e2a49e468cb059868479cafebeb804f6b855423d" dependencies = [ "ecdsa", "elliptic-curve", + "primefield", "primeorder", "sha2", ] [[package]] name = "p384" -version = "0.13.1" +version = "0.14.0-pre.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +checksum = "701032b3730df6b882496d6cee8221de0ce4bc11ddc64e6d89784aa5b8a6de30" dependencies = [ "ecdsa", "elliptic-curve", + "fiat-crypto", + "primefield", "primeorder", "sha2", ] [[package]] name = "p521" -version = "0.13.3" +version = "0.14.0-pre.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" +checksum = "40ba29c2906eb5c89a8c411c4f11243ee4e5517ee7d71d9a13fedc877a6057b1" dependencies = [ "base16ct", "ecdsa", "elliptic-curve", + "primefield", "primeorder", - "rand_core 0.6.4", + "rand_core 0.9.3", "sha2", ] [[package]] -name = "parking_lot" -version = "0.12.4" +name = "page_size" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -3787,15 +3868,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.17", + "redox_syscall 0.5.18", "smallvec", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -3806,13 +3887,13 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pbkdf2" -version = "0.12.2" +version = "0.13.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +checksum = "ca3fc18bb4460ac250ba6b75dfa7cf9d0b2273e3e623f660bd6ce2c3e902342e" dependencies = [ - "digest", + "digest 0.11.0-rc.3", "hmac", - "sha1", + "sha1 0.11.0-rc.2", ] [[package]] @@ -3824,6 +3905,15 @@ dependencies = [ "base64ct", ] +[[package]] +name = "pem-rfc7468" +version = "1.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8e58fab693c712c0d4e88f8eb3087b6521d060bcaf76aeb20cb192d809115ba" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -3832,40 +3922,69 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "picky" -version = "7.0.0-rc.17" +version = "7.0.0-rc.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33807ce79d4b14a8918e968a8606e5142ddc6aec933acef79de0bd769cae5fb1" +checksum = "4cdc52be663aebd70d7006ae305c87eb32a2b836d6c2f26f7e384f845d80b621" dependencies = [ + "aead", "aes", "aes-gcm", "aes-kw", "base64", + "block-buffer 0.11.0-rc.5", + "block-padding", "cbc", + "cipher", + "crypto-bigint", + "crypto-common 0.2.0-rc.4", + "crypto-primes", + "ctr", + "curve25519-dalek", + "der 0.8.0-rc.9", "des", - "digest", + "digest 0.11.0-rc.3", + "ecdsa", + "ed25519", "ed25519-dalek", + "elliptic-curve", + "ff", + "ghash", + "group", "hex", + "hkdf", "hmac", "http", - "md-5", - "num-bigint-dig", + "inout", + "keccak", + "md-5 0.11.0-rc.2", "p256", "p384", "p521", "pbkdf2", + "pem-rfc7468 1.0.0-rc.3", "picky-asn1", "picky-asn1-der", "picky-asn1-x509", - "rand 0.8.5", - "rand_core 0.6.4", + "pkcs1 0.8.0-rc.4", + "pkcs8", + "polyval", + "primefield", + "primeorder", + "rand 0.9.2", + "rand_core 0.9.3", "rc2", + "rfc6979", "rsa", + "sec1", "serde", "serde_json", - "sha1", + "sha1 0.11.0-rc.2", "sha2", "sha3", - "thiserror 1.0.69", + "signature", + "spki 0.8.0-rc.4", + "thiserror 2.0.17", + "universal-hash", "x25519-dalek", "zeroize", ] @@ -3885,9 +4004,9 @@ dependencies = [ [[package]] name = "picky-asn1-der" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c9c33310628f2e1758bbde11b16713505e188a859ab7990f98eb015b943bf5" +checksum = "b491eb61603cba1ad5c6be0269883538f8d74136c35e3641a840fb0fbcd41efc" dependencies = [ "picky-asn1", "serde", @@ -3896,12 +4015,12 @@ dependencies = [ [[package]] name = "picky-asn1-x509" -version = "0.14.6" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d493f73cf052073ca1fe38666f74c2396987aa6ea660e77dd624cc6c8f60389e" +checksum = "c97cd14d567a17755910fa8718277baf39d08682a980b1b1a4b4da7d0bc61a04" dependencies = [ "base64", - "num-bigint-dig", + "crypto-bigint", "oid", "picky-asn1", "picky-asn1-der", @@ -3912,26 +4031,31 @@ dependencies = [ [[package]] name = "picky-krb" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e78a55491723b0a10bc2c02709a8d92d74ef674fe1b569cb4a08bac3d105487" +checksum = "9ed61c8d7448649c031ecae02afb10c679524c7a9af5fb0fbee466b3cc0d6df1" dependencies = [ "aes", + "block-buffer 0.11.0-rc.5", + "block-padding", "byteorder", "cbc", - "crypto", + "cipher", + "crypto-bigint", + "crypto-common 0.2.0-rc.4", "des", + "digest 0.11.0-rc.3", "hmac", - "num-bigint-dig", + "inout", "oid", "pbkdf2", "picky-asn1", "picky-asn1-der", "picky-asn1-x509", - "rand 0.8.5", + "rand 0.9.2", "serde", - "sha1", - "thiserror 1.0.69", + "sha1 0.11.0-rc.2", + "thiserror 2.0.17", "uuid", ] @@ -3979,19 +4103,28 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" dependencies = [ - "der", - "pkcs8", - "spki", + "der 0.7.10", + "spki 0.7.3", +] + +[[package]] +name = "pkcs1" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986d2e952779af96ea048f160fd9194e1751b4faea78bcf3ceb456efe008088e" +dependencies = [ + "der 0.8.0-rc.9", + "spki 0.8.0-rc.4", ] [[package]] name = "pkcs8" -version = "0.10.2" +version = "0.11.0-rc.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +checksum = "93eac55f10aceed84769df670ea4a32d2ffad7399400d41ee1c13b1cd8e1b478" dependencies = [ - "der", - "spki", + "der 0.8.0-rc.9", + "spki 0.8.0-rc.4", ] [[package]] @@ -4034,7 +4167,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "crc32fast", "fdeflate", "flate2", @@ -4052,21 +4185,26 @@ dependencies = [ "hermit-abi", "pin-project-lite", "rustix 1.1.2", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] name = "polyval" -version = "0.6.2" +version = "0.7.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +checksum = "1ffd40cc99d0fbb02b4b3771346b811df94194bc103983efa0203c8893755085" dependencies = [ "cfg-if", "cpufeatures", - "opaque-debug", "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + [[package]] name = "portpicker" version = "0.1.1" @@ -4078,9 +4216,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -4111,20 +4249,23 @@ dependencies = [ ] [[package]] -name = "prettyplease" -version = "0.2.37" +name = "primefield" +version = "0.14.0-pre.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +checksum = "d7fcd4a163053332fd93f39b81c133e96a98567660981654579c90a99062fbf5" dependencies = [ - "proc-macro2", - "syn", + "crypto-bigint", + "ff", + "rand_core 0.9.3", + "subtle", + "zeroize", ] [[package]] name = "primeorder" -version = "0.13.6" +version = "0.14.0-pre.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +checksum = "1c36e8766fcd270fa9c665b9dc364f570695f5a59240949441b077a397f15b74" dependencies = [ "elliptic-curve", ] @@ -4146,23 +4287,22 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb0be07becd10686a0bb407298fb425360a5c44a663774406340c59a22de4ce" +checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.9.4", - "lazy_static", + "bitflags 2.10.0", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", @@ -4175,9 +4315,9 @@ dependencies = [ [[package]] name = "pxfm" -version = "0.1.24" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83f9b339b02259ada5c0f4a389b7fb472f933aa17ce176fd2ad98f28bb401fde" +checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8" dependencies = [ "num-traits", ] @@ -4219,7 +4359,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.0", + "socket2 0.6.1", "thiserror 2.0.17", "tokio", "tracing", @@ -4233,7 +4373,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", - "getrandom 0.3.3", + "getrandom 0.3.4", "lru-slab", "rand 0.9.2", "ring", @@ -4256,16 +4396,16 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.0", + "socket2 0.6.1", "tracing", "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.41" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -4338,7 +4478,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", ] [[package]] @@ -4378,9 +4518,9 @@ dependencies = [ [[package]] name = "rc2" -version = "0.8.1" +version = "0.9.0-pre.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62c64daa8e9438b84aaae55010a93f396f8e60e3911590fcba770d04643fc1dd" +checksum = "b03621ac292cc723def9e0fd0eb9573b1df8d6a9ee7ad637fe94dfc153705f3c" dependencies = [ "cipher", ] @@ -4396,18 +4536,27 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.17" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", +] + +[[package]] +name = "redox_syscall" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5" +dependencies = [ + "bitflags 2.10.0", ] [[package]] name = "regex" -version = "1.11.3" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -4417,9 +4566,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.11" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -4428,9 +4577,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "relative-path" @@ -4440,9 +4589,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "reqwest" -version = "0.12.23" +version = "0.12.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" dependencies = [ "base64", "bytes", @@ -4493,15 +4642,15 @@ dependencies = [ [[package]] name = "resolv-conf" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b3789b30bd25ba102de4beabd95d21ac45b69b1be7d14522bab988c526d6799" +checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" [[package]] name = "rfc6979" -version = "0.4.0" +version = "0.5.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +checksum = "d369f9c4f79388704648e7bcb92749c0d6cf4397039293a9b747694fa4fb4bae" dependencies = [ "hmac", "subtle", @@ -4532,42 +4681,40 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.8" +version = "0.10.0-rc.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +checksum = "bf8955ab399f6426998fde6b76ae27233cce950705e758a6c17afd2f6d0e5d52" dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", + "const-oid 0.10.1", + "crypto-bigint", + "crypto-primes", + "digest 0.11.0-rc.3", + "pkcs1 0.8.0-rc.4", "pkcs8", - "rand_core 0.6.4", - "sha1", + "rand_core 0.9.3", + "sha1 0.11.0-rc.2", "signature", - "spki", + "spki 0.8.0-rc.4", "subtle", "zeroize", ] [[package]] name = "rstest" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fc39292f8613e913f7df8fa892b8944ceb47c247b78e1b1ae2f09e019be789d" +checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" dependencies = [ "futures-timer", "futures-util", "rstest_macros", - "rustc_version", ] [[package]] name = "rstest_macros" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f168d99749d307be9de54d23fd226628d99768225ef08f6ffb52e0182a27746" +checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" dependencies = [ "cfg-if", "glob", @@ -4581,12 +4728,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - [[package]] name = "rustc-hash" version = "2.1.1" @@ -4617,7 +4758,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -4630,18 +4771,18 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.32" +version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "aws-lc-rs", "log", @@ -4655,9 +4796,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -4676,9 +4817,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" dependencies = [ "web-time", "zeroize", @@ -4686,9 +4827,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.7" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "aws-lc-rs", "ring", @@ -4704,9 +4845,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-fork" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" dependencies = [ "fnv", "quick-error", @@ -4735,7 +4876,7 @@ version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -4765,25 +4906,33 @@ dependencies = [ [[package]] name = "sec1" -version = "0.7.3" +version = "0.8.0-rc.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +checksum = "1dff52f6118bc9f0ac974a54a639d499ac26a6cad7a6e39bc0990c19625e793b" dependencies = [ "base16ct", - "der", - "generic-array", - "pkcs8", + "der 0.8.0-rc.9", + "hybrid-array", "subtle", "zeroize", ] +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + [[package]] name = "security-framework" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -4796,7 +4945,7 @@ version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -4874,9 +5023,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" dependencies = [ "serde_core", ] @@ -4893,6 +5042,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serdect" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3ef0e35b322ddfaecbc60f34ab448e157e48531288ee49fafbb053696b8ffe2" +dependencies = [ + "base16ct", + "serde", +] + [[package]] name = "sha1" version = "0.10.6" @@ -4901,27 +5060,38 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", +] + +[[package]] +name = "sha1" +version = "0.11.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e046edf639aa2e7afb285589e5405de2ef7e61d4b0ac1e30256e3eab911af9" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.11.0-rc.3", ] [[package]] name = "sha2" -version = "0.10.9" +version = "0.11.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +checksum = "d1e3878ab0f98e35b2df35fe53201d088299b41a6bb63e3e34dada2ac4abd924" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.11.0-rc.3", ] [[package]] name = "sha3" -version = "0.10.8" +version = "0.11.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +checksum = "2103ca0e6f4e9505eae906de5e5883e06fc3b2232fb5d6914890c7bbcb62f478" dependencies = [ - "digest", + "digest 0.11.0-rc.3", "keccak", ] @@ -4952,9 +5122,9 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" dependencies = [ "libc", "mio", @@ -4963,28 +5133,28 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" dependencies = [ "libc", ] [[package]] name = "signature" -version = "2.2.0" +version = "3.0.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +checksum = "fc280a6ff65c79fbd6622f64d7127f32b85563bca8c53cd2e9141d6744a9056d" dependencies = [ - "digest", - "rand_core 0.6.4", + "digest 0.11.0-rc.3", + "rand_core 0.9.3", ] [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "slab" @@ -5004,7 +5174,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "calloop", "calloop-wayland-source", "cursor-icon", @@ -5044,43 +5214,43 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "softbuffer" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" +checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" dependencies = [ "as-raw-xcb-connection", "bytemuck", - "cfg_aliases", - "core-graphics 0.24.0", "drm", "fastrand", - "foreign-types 0.5.0", "js-sys", - "log", "memmap2", - "objc2 0.5.2", - "objc2-foundation 0.2.2", - "objc2-quartz-core", + "ndk", + "objc2 0.6.3", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", "raw-window-handle", - "redox_syscall 0.5.17", - "rustix 0.38.44", + "redox_syscall 0.5.18", + "rustix 1.1.2", "tiny-xlib", + "tracing", "wasm-bindgen", "wayland-backend", "wayland-client", "wayland-sys", "web-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", "x11rb", ] @@ -5089,6 +5259,9 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] name = "spki" @@ -5097,62 +5270,92 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der", + "der 0.7.10", +] + +[[package]] +name = "spki" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8baeff88f34ed0691978ec34440140e1572b68c7dd4a495fd14a3dc1944daa80" +dependencies = [ + "base64ct", + "der 0.8.0-rc.9", ] [[package]] name = "sspi" -version = "0.16.1" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523f6a99e26c1e6476a424d54bbda5354a01ee7f18b9d93dc48a8fd45ae8189b" +checksum = "43f73fe6be958ae27fa8e982d9acc42d16f34eb74714d95bb53015528667cae4" dependencies = [ "async-dnssd", "async-recursion", - "bitflags 2.9.4", + "bitflags 2.10.0", + "block-buffer 0.11.0-rc.5", "byteorder", "cfg-if", + "crypto-bigint", + "crypto-common 0.2.0-rc.4", "crypto-mac", + "crypto-primes", + "cryptoki", + "curve25519-dalek", + "der 0.8.0-rc.9", + "digest 0.11.0-rc.3", + "ed25519-dalek", + "ff", "futures", + "getrandom 0.3.4", + "group", + "hickory-proto", "hickory-resolver", "hmac", - "lazy_static", - "md-5", + "md-5 0.11.0-rc.2", "md4", - "num-bigint-dig", "num-derive", "num-traits", "oid", + "p256", + "p384", + "p521", + "pem-rfc7468 1.0.0-rc.3", "picky", "picky-asn1", "picky-asn1-der", "picky-asn1-x509", "picky-krb", + "pkcs1 0.8.0-rc.4", + "pkcs8", "portpicker", - "rand 0.8.5", + "primefield", + "primeorder", + "rand 0.9.2", "reqwest", "rsa", "rustls", "rustls-native-certs", "serde", - "serde_derive", - "sha1", + "sha1 0.11.0-rc.2", "sha2", + "signature", + "spki 0.8.0-rc.4", "time", "tokio", "tracing", "url", "uuid", - "windows 0.61.3", + "windows 0.62.2", "windows-registry", - "windows-sys 0.60.2", + "winscard", "zeroize", ] [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "strck" @@ -5190,9 +5393,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.106" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -5225,7 +5428,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -5240,6 +5443,12 @@ dependencies = [ "libc", ] +[[package]] +name = "tagptr" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" + [[package]] name = "tap" version = "1.0.1" @@ -5253,10 +5462,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", "rustix 1.1.2", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -5386,9 +5595,9 @@ checksum = "9ab95735ea2c8fd51154d01e39cf13912a78071c2d89abc49a7ef102a7dd725a" [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -5442,29 +5651,26 @@ dependencies = [ [[package]] name = "tokio" -version = "1.47.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "slab", - "socket2 0.6.0", + "socket2 0.6.1", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", @@ -5493,9 +5699,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489a59b6730eda1b0171fcfda8b121f4bee2b35cba8645ca35c5f7ba3eb736c1" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" dependencies = [ "futures-util", "log", @@ -5511,9 +5717,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ "bytes", "futures-core", @@ -5524,9 +5730,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.7" +version = "0.9.9+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0" +checksum = "eb5238e643fc34a1d5d7e753e1532a91912d74b63b92b3ea51fde8d1b7bc79dd" dependencies = [ "indexmap", "serde_core", @@ -5539,18 +5745,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.2" +version = "0.7.4+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" +checksum = "fe3cea6b2aa3b910092f6abd4053ea464fab5f9c170ba5e9a6aead16ec4af2b6" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.23.6" +version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ "indexmap", "toml_datetime", @@ -5560,18 +5766,18 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.3" +version = "1.0.5+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" +checksum = "4c03bee5ce3696f31250db0bbaff18bc43301ce0e8db2ed1f07cbb2acf89984c" dependencies = [ "winnow", ] [[package]] name = "toml_writer" -version = "1.0.3" +version = "1.0.5+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109" +checksum = "a9cd6190959dce0994aa8970cd32ab116d1851ead27e866039acaf2524ce44fa" [[package]] name = "tower" @@ -5590,11 +5796,11 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "bytes", "futures-util", "http", @@ -5620,9 +5826,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" dependencies = [ "log", "pin-project-lite", @@ -5632,9 +5838,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", @@ -5643,9 +5849,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" dependencies = [ "once_cell", "valuable", @@ -5664,9 +5870,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "matchers", "nu-ansi-term", @@ -5723,9 +5929,9 @@ checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" [[package]] name = "tungstenite" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", @@ -5736,16 +5942,16 @@ dependencies = [ "rand 0.9.2", "rustls", "rustls-pki-types", - "sha1", + "sha1 0.10.6", "thiserror 2.0.17", "utf-8", ] [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unarray" @@ -5755,9 +5961,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-segmentation" @@ -5767,17 +5973,17 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "universal-hash" -version = "0.5.1" +version = "0.6.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +checksum = "a55be643b40a21558f44806b53ee9319595bc7ca6896372e4e08e5d7d83c9cd6" dependencies = [ - "crypto-common", + "crypto-common 0.2.0-rc.4", "subtle", ] @@ -5819,13 +6025,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "js-sys", - "serde", + "serde_core", "wasm-bindgen", ] @@ -5912,15 +6118,6 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[package]] -name = "wasi" -version = "0.14.7+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" -dependencies = [ - "wasip2", -] - [[package]] name = "wasip2" version = "1.0.1+wasi-0.2.4" @@ -5938,9 +6135,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.104" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", @@ -5949,25 +6146,11 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - [[package]] name = "wasm-bindgen-futures" -version = "0.4.54" +version = "0.4.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" dependencies = [ "cfg-if", "js-sys", @@ -5978,9 +6161,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.104" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5988,22 +6171,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.104" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.104" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] @@ -6028,7 +6211,7 @@ version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "rustix 1.1.2", "wayland-backend", "wayland-scanner", @@ -6040,7 +6223,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "cursor-icon", "wayland-backend", ] @@ -6062,7 +6245,7 @@ version = "0.32.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -6074,7 +6257,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6087,7 +6270,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -6119,9 +6302,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.81" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", @@ -6139,9 +6322,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" dependencies = [ "rustls-pki-types", ] @@ -6159,9 +6342,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" [[package]] name = "winapi" @@ -6185,7 +6368,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -6206,24 +6389,23 @@ dependencies = [ [[package]] name = "windows" -version = "0.61.3" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ "windows-collections", - "windows-core 0.61.2", + "windows-core 0.62.2", "windows-future", - "windows-link 0.1.3", "windows-numerics", ] [[package]] name = "windows-collections" -version = "0.2.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-core 0.61.2", + "windows-core 0.62.2", ] [[package]] @@ -6238,46 +6420,33 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.61.2" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", -] - -[[package]] -name = "windows-core" -version = "0.62.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6844ee5416b285084d3d3fffd743b925a6c9385455f64f6d4fa3031c4c2749a9" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link 0.2.0", - "windows-result 0.4.0", - "windows-strings 0.5.0", + "windows-link", + "windows-result 0.4.1", + "windows-strings", ] [[package]] name = "windows-future" -version = "0.2.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", + "windows-core 0.62.2", + "windows-link", "windows-threading", ] [[package]] name = "windows-implement" -version = "0.60.1" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb307e42a74fb6de9bf3a02d9712678b22399c87e6fa869d6dfcd8c1b7754e0" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", @@ -6286,9 +6455,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.59.2" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0abd1ddbc6964ac14db11c7213d6532ef34bd9aa042c2e5935f59d7908b46a5" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", @@ -6297,35 +6466,29 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.3" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - -[[package]] -name = "windows-link" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-numerics" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", + "windows-core 0.62.2", + "windows-link", ] [[package]] name = "windows-registry" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", + "windows-link", + "windows-result 0.4.1", + "windows-strings", ] [[package]] @@ -6339,38 +6502,20 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.3.4" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-result" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" -dependencies = [ - "windows-link 0.2.0", + "windows-link", ] [[package]] name = "windows-strings" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-strings" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" -dependencies = [ - "windows-link 0.2.0", + "windows-link", ] [[package]] @@ -6415,16 +6560,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.4", + "windows-targets 0.53.5", ] [[package]] name = "windows-sys" -version = "0.61.1" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.0", + "windows-link", ] [[package]] @@ -6475,28 +6620,28 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.4" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d42b7b7f66d2a06854650af09cfdf8713e427a439c97ad65a6375318033ac4b" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link 0.2.0", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] name = "windows-threading" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -6519,9 +6664,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -6543,9 +6688,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -6567,9 +6712,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -6579,9 +6724,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -6603,9 +6748,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -6627,9 +6772,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -6651,9 +6796,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -6675,9 +6820,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winit" @@ -6688,14 +6833,14 @@ dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.9.4", + "bitflags 2.10.0", "block2", "bytemuck", "calloop", "cfg_aliases", "concurrent-queue", "core-foundation 0.9.4", - "core-graphics 0.23.2", + "core-graphics", "cursor-icon", "dpi", "js-sys", @@ -6733,9 +6878,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -6760,6 +6905,29 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "winscard" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b6ec4e6176df62589d1ac9950f6295be87ca06ee61a7c9a579a2bcc80efe34" +dependencies = [ + "bitflags 2.10.0", + "crypto-bigint", + "flate2", + "iso7816", + "iso7816-tlv", + "num-derive", + "num-traits", + "picky", + "picky-asn1-x509", + "rand_core 0.9.3", + "rsa", + "sha1 0.11.0-rc.2", + "time", + "tracing", + "uuid", +] + [[package]] name = "wit-bindgen" version = "0.46.0" @@ -6768,9 +6936,9 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "wyz" @@ -6815,12 +6983,12 @@ checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" [[package]] name = "x25519-dalek" -version = "2.0.1" +version = "3.0.0-pre.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +checksum = "3a45998121837fd8c92655d2334aa8f3e5ef0645cdfda5b321b13760c548fd55" dependencies = [ "curve25519-dalek", - "rand_core 0.6.4", + "rand_core 0.9.3", "serde", "zeroize", ] @@ -6831,9 +6999,9 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" dependencies = [ - "const-oid", - "der", - "spki", + "const-oid 0.9.6", + "der 0.7.10", + "spki 0.7.3", "tls_codec", ] @@ -6849,7 +7017,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.9.4", + "bitflags 2.10.0", "dlib", "log", "once_cell", @@ -6895,11 +7063,10 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -6907,9 +7074,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", @@ -6919,27 +7086,27 @@ dependencies = [ [[package]] name = "yuv" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30078f3e5790a2127f89c57c4ccb46d060205bf2c4c267f77cd08fb5c02c6d79" +checksum = "28f1bad143caadcfcaec93039dc9c40a30fc86f23d9e7cc03764a39fe51d9d43" dependencies = [ "num-traits", ] [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ "proc-macro2", "quote", @@ -6989,9 +7156,9 @@ dependencies = [ [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -7000,9 +7167,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -7011,9 +7178,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 14037b6b..72904a94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,12 +34,11 @@ categories = ["network-programming"] # even for private dependencies. expect-test = "1" proptest = "1.4" -rstest = "0.25" +rstest = "0.26" # Note: we are trying to move away from using these crates. # They are being kept around for now for legacy compatibility, # 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" @@ -93,7 +92,7 @@ fn_to_numeric_cast_any = "warn" ptr_cast_constness = "warn" # == Correctness == # -#as_conversions = "warn" +as_conversions = "warn" cast_lossless = "warn" cast_possible_truncation = "warn" cast_possible_wrap = "warn" @@ -128,6 +127,7 @@ empty_enum_variants_with_brackets = "warn" deref_by_slicing = "warn" multiple_inherent_impl = "warn" map_with_unused_argument_over_ranges = "warn" +partial_pub_fields = "warn" trait_duplication_in_bounds = "warn" type_repetition_in_bounds = "warn" checked_conversions = "warn" @@ -156,6 +156,7 @@ or_fun_call = "warn" rc_buffer = "warn" string_lit_chars_any = "warn" unnecessary_box_returns = "warn" +large_futures = "warn" # == Extra-pedantic clippy == # allow_attributes = "warn" diff --git a/benches/Cargo.toml b/benches/Cargo.toml index deb14ee8..19a87c6f 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -17,7 +17,7 @@ qoiz = ["ironrdp/qoiz"] [dependencies] anyhow = "1.0.99" async-trait = "0.1.89" -bytesize = "2.1.0" +bytesize = "2.3" ironrdp = { path = "../crates/ironrdp", features = [ "server", "pdu", 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/CHANGELOG.md b/crates/ironrdp-acceptor/CHANGELOG.md index d915be30..4918e46f 100644 --- a/crates/ironrdp-acceptor/CHANGELOG.md +++ b/crates/ironrdp-acceptor/CHANGELOG.md @@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.8.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-acceptor-v0.7.0...ironrdp-acceptor-v0.8.0)] - 2025-12-18 + +### Bug Fixes + +- [**breaking**] Use static dispatch for NetworkClient trait ([#1043](https://github.com/Devolutions/IronRDP/issues/1043)) ([bca6d190a8](https://github.com/Devolutions/IronRDP/commit/bca6d190a870708468534d224ff225a658767a9a)) + + - Rename `AsyncNetworkClient` to `NetworkClient` + - Replace dynamic dispatch (`Option<&mut dyn ...>`) with static dispatch + using generics (`&mut N where N: NetworkClient`) + - Reorder `connect_finalize` parameters for consistency across crates + ## [[0.6.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-acceptor-v0.5.0...ironrdp-acceptor-v0.6.0)] - 2025-07-08 ### Features diff --git a/crates/ironrdp-acceptor/Cargo.toml b/crates/ironrdp-acceptor/Cargo.toml index a4ef47db..a3c42b9d 100644 --- a/crates/ironrdp-acceptor/Cargo.toml +++ b/crates/ironrdp-acceptor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-acceptor" -version = "0.7.0" +version = "0.8.0" readme = "README.md" description = "State machines to drive an RDP connection acceptance sequence" edition.workspace = true @@ -19,8 +19,8 @@ test = false ironrdp-core = { path = "../ironrdp-core", version = "0.1", features = ["alloc"] } # public ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6" } # public ironrdp-svc = { path = "../ironrdp-svc", version = "0.5" } # public -ironrdp-connector = { path = "../ironrdp-connector", version = "0.7" } # public -ironrdp-async = { path = "../ironrdp-async", version = "0.7" } # public +ironrdp-connector = { path = "../ironrdp-connector", version = "0.8" } # public +ironrdp-async = { path = "../ironrdp-async", version = "0.8" } # public tracing = { version = "0.1", features = ["log"] } [lints] diff --git a/crates/ironrdp-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/credssp.rs b/crates/ironrdp-acceptor/src/credssp.rs index f8840780..cf50ab56 100644 --- a/crates/ironrdp-acceptor/src/credssp.rs +++ b/crates/ironrdp-acceptor/src/credssp.rs @@ -1,4 +1,4 @@ -use ironrdp_async::AsyncNetworkClient; +use ironrdp_async::NetworkClient; use ironrdp_connector::sspi::credssp::{ CredSspServer, CredentialsProxy, ServerError, ServerMode, ServerState, TsRequest, }; @@ -71,7 +71,7 @@ impl CredentialsProxy for CredentialsProxyImpl<'_> { pub(crate) async fn resolve_generator( generator: &mut CredsspProcessGenerator<'_>, - network_client: &mut dyn AsyncNetworkClient, + network_client: &mut impl NetworkClient, ) -> Result { let mut state = generator.start(); diff --git a/crates/ironrdp-acceptor/src/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-acceptor/src/lib.rs b/crates/ironrdp-acceptor/src/lib.rs index aea782a4..32bc2710 100644 --- a/crates/ironrdp-acceptor/src/lib.rs +++ b/crates/ironrdp-acceptor/src/lib.rs @@ -1,7 +1,7 @@ #![cfg_attr(doc, doc = include_str!("../README.md"))] #![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")] -use ironrdp_async::{single_sequence_step, AsyncNetworkClient, Framed, FramedRead, FramedWrite, StreamWrapper}; +use ironrdp_async::{single_sequence_step, Framed, FramedRead, FramedWrite, NetworkClient, StreamWrapper}; use ironrdp_connector::sspi::credssp::EarlyUserAuthResult; use ironrdp_connector::sspi::{AuthIdentity, KerberosServerConfig, Username}; use ironrdp_connector::{custom_err, general_err, ConnectorResult, ServerName}; @@ -51,16 +51,17 @@ where } } -pub async fn accept_credssp( +pub async fn accept_credssp( framed: &mut Framed, acceptor: &mut Acceptor, + network_client: &mut N, client_computer_name: ServerName, public_key: Vec, kerberos_config: Option, - network_client: Option<&mut dyn AsyncNetworkClient>, ) -> ConnectorResult<()> where S: FramedRead + FramedWrite, + N: NetworkClient, { let mut buf = WriteBuf::new(); @@ -68,11 +69,11 @@ where perform_credssp_step( framed, acceptor, + network_client, &mut buf, client_computer_name, public_key, kerberos_config, - network_client, ) .await } else { @@ -98,34 +99,73 @@ where } #[instrument(level = "trace", skip_all, ret)] -async fn perform_credssp_step( +async fn perform_credssp_step( framed: &mut Framed, acceptor: &mut Acceptor, + network_client: &mut N, buf: &mut WriteBuf, client_computer_name: ServerName, public_key: Vec, kerberos_config: Option, - network_client: Option<&mut dyn AsyncNetworkClient>, ) -> ConnectorResult<()> where S: FramedRead + FramedWrite, + N: NetworkClient, { assert!(acceptor.should_perform_credssp()); let AcceptorState::Credssp { protocol, .. } = acceptor.state else { unreachable!() }; - async fn credssp_loop( + let result = credssp_loop( + framed, + acceptor, + network_client, + buf, + client_computer_name, + public_key, + kerberos_config, + ) + .await; + + if protocol.intersects(nego::SecurityProtocol::HYBRID_EX) { + trace!(?result, "HYBRID_EX"); + + let result = if result.is_ok() { + EarlyUserAuthResult::Success + } else { + EarlyUserAuthResult::AccessDenied + }; + + buf.clear(); + result + .to_buffer(&mut *buf) + .map_err(|e| ironrdp_connector::custom_err!("to_buffer", e))?; + let response = &buf[..result.buffer_len()]; + framed + .write_all(response) + .await + .map_err(|e| ironrdp_connector::custom_err!("write all", e))?; + } + + result?; + + acceptor.mark_credssp_as_done(); + + return Ok(()); + + async fn credssp_loop( framed: &mut Framed, acceptor: &mut Acceptor, + network_client: &mut N, buf: &mut WriteBuf, client_computer_name: ServerName, public_key: Vec, kerberos_config: Option, - mut network_client: Option<&mut dyn AsyncNetworkClient>, ) -> ConnectorResult<()> where S: FramedRead + FramedWrite, + N: NetworkClient, { let creds = acceptor .creds @@ -164,12 +204,7 @@ where let result = { let mut generator = sequence.process_ts_request(ts_request); - - if let Some(network_client_ref) = network_client.as_deref_mut() { - resolve_generator(&mut generator, network_client_ref).await - } else { - generator.resolve_to_result() - } + resolve_generator(&mut generator, network_client).await }; // drop generator buf.clear(); @@ -184,43 +219,7 @@ where .map_err(|e| ironrdp_connector::custom_err!("write all", e))?; } } + Ok(()) } - - let result = credssp_loop( - framed, - acceptor, - buf, - client_computer_name, - public_key, - kerberos_config, - network_client, - ) - .await; - - if protocol.intersects(nego::SecurityProtocol::HYBRID_EX) { - trace!(?result, "HYBRID_EX"); - - let result = if result.is_ok() { - EarlyUserAuthResult::Success - } else { - EarlyUserAuthResult::AccessDenied - }; - - buf.clear(); - result - .to_buffer(&mut *buf) - .map_err(|e| ironrdp_connector::custom_err!("to_buffer", e))?; - let response = &buf[..result.buffer_len()]; - framed - .write_all(response) - .await - .map_err(|e| ironrdp_connector::custom_err!("write all", e))?; - } - - result?; - - acceptor.mark_credssp_as_done(); - - Ok(()) } diff --git a/crates/ironrdp-async/CHANGELOG.md b/crates/ironrdp-async/CHANGELOG.md index b83ee99e..d98372c7 100644 --- a/crates/ironrdp-async/CHANGELOG.md +++ b/crates/ironrdp-async/CHANGELOG.md @@ -6,14 +6,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.8.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-async-v0.7.0...ironrdp-async-v0.8.0)] - 2025-12-18 + +### Bug Fixes + +- [**breaking**] Use static dispatch for NetworkClient trait ([#1043](https://github.com/Devolutions/IronRDP/issues/1043)) ([bca6d190a8](https://github.com/Devolutions/IronRDP/commit/bca6d190a870708468534d224ff225a658767a9a)) + + - Rename `AsyncNetworkClient` to `NetworkClient` + - Replace dynamic dispatch (`Option<&mut dyn ...>`) with static dispatch + using generics (`&mut N where N: NetworkClient`) + - Reorder `connect_finalize` parameters for consistency across crates + ## [[0.3.2](https://github.com/Devolutions/IronRDP/compare/ironrdp-async-v0.3.1...ironrdp-async-v0.3.2)] - 2025-03-12 ### Build - Bump ironrdp-pdu - - ## [[0.3.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-async-v0.3.0...ironrdp-async-v0.3.1)] - 2025-03-12 ### Build diff --git a/crates/ironrdp-async/Cargo.toml b/crates/ironrdp-async/Cargo.toml index 31a81d01..1716abb4 100644 --- a/crates/ironrdp-async/Cargo.toml +++ b/crates/ironrdp-async/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-async" -version = "0.7.0" +version = "0.8.0" readme = "README.md" description = "Provides `Future`s wrapping the IronRDP state machines conveniently" edition.workspace = true @@ -16,7 +16,7 @@ doctest = false test = false [dependencies] -ironrdp-connector = { path = "../ironrdp-connector", version = "0.7" } # public +ironrdp-connector = { path = "../ironrdp-connector", version = "0.8" } # public ironrdp-core = { path = "../ironrdp-core", version = "0.1", features = ["alloc"] } # public ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6" } # public tracing = { version = "0.1", features = ["log"] } diff --git a/crates/ironrdp-async/src/connector.rs b/crates/ironrdp-async/src/connector.rs index 823cfe10..04f3d5a7 100644 --- a/crates/ironrdp-async/src/connector.rs +++ b/crates/ironrdp-async/src/connector.rs @@ -2,14 +2,14 @@ use ironrdp_connector::credssp::{CredsspProcessGenerator, CredsspSequence, Kerbe use ironrdp_connector::sspi::credssp::ClientState; use ironrdp_connector::sspi::generator::GeneratorState; use ironrdp_connector::{ - custom_err, general_err, ClientConnector, ClientConnectorState, ConnectionResult, ConnectorError, ConnectorResult, - ServerName, State as _, + general_err, ClientConnector, ClientConnectorState, ConnectionResult, ConnectorError, ConnectorResult, ServerName, + State as _, }; use ironrdp_core::WriteBuf; use tracing::{debug, info, instrument, trace}; use crate::framed::{Framed, FramedRead, FramedWrite}; -use crate::{single_sequence_step, AsyncNetworkClient}; +use crate::{single_sequence_step, NetworkClient}; #[non_exhaustive] pub struct ShouldUpgrade; @@ -49,28 +49,29 @@ pub fn mark_as_upgraded(_: ShouldUpgrade, connector: &mut ClientConnector) -> Up } #[instrument(skip_all)] -pub async fn connect_finalize( +pub async fn connect_finalize( _: Upgraded, - framed: &mut Framed, mut connector: ClientConnector, + framed: &mut Framed, + network_client: &mut N, server_name: ServerName, server_public_key: Vec, - network_client: Option<&mut dyn AsyncNetworkClient>, kerberos_config: Option, ) -> ConnectorResult where S: FramedRead + FramedWrite, + N: NetworkClient, { let mut buf = WriteBuf::new(); if connector.should_perform_credssp() { perform_credssp_step( - framed, &mut connector, + framed, + network_client, &mut buf, server_name, server_public_key, - network_client, kerberos_config, ) .await?; @@ -91,7 +92,7 @@ where async fn resolve_generator( generator: &mut CredsspProcessGenerator<'_>, - network_client: &mut dyn AsyncNetworkClient, + network_client: &mut impl NetworkClient, ) -> ConnectorResult { let mut state = generator.start(); @@ -110,17 +111,18 @@ async fn resolve_generator( } #[instrument(level = "trace", skip_all)] -async fn perform_credssp_step( - framed: &mut Framed, +async fn perform_credssp_step( connector: &mut ClientConnector, + framed: &mut Framed, + network_client: &mut N, buf: &mut WriteBuf, server_name: ServerName, server_public_key: Vec, - mut network_client: Option<&mut dyn AsyncNetworkClient>, kerberos_config: Option, ) -> ConnectorResult<()> where S: FramedRead + FramedWrite, + N: NetworkClient, { assert!(connector.should_perform_credssp()); @@ -141,15 +143,8 @@ where loop { let client_state = { let mut generator = sequence.process_ts_request(ts_request); - - if let Some(network_client_ref) = network_client.as_deref_mut() { - trace!("resolving network"); - resolve_generator(&mut generator, network_client_ref).await? - } else { - generator - .resolve_to_result() - .map_err(|e| custom_err!("resolve without network client", e))? - } + trace!("resolving network"); + resolve_generator(&mut generator, network_client).await? }; // drop generator buf.clear(); diff --git a/crates/ironrdp-async/src/lib.rs b/crates/ironrdp-async/src/lib.rs index b3677535..847200c0 100644 --- a/crates/ironrdp-async/src/lib.rs +++ b/crates/ironrdp-async/src/lib.rs @@ -1,15 +1,14 @@ #![cfg_attr(doc, doc = include_str!("../README.md"))] #![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")] +use core::future::Future; + pub use bytes; mod connector; mod framed; mod session; -use core::future::Future; -use core::pin::Pin; - use ironrdp_connector::sspi::generator::NetworkRequest; use ironrdp_connector::ConnectorResult; @@ -17,9 +16,6 @@ pub use self::connector::*; pub use self::framed::*; // pub use self::session::*; -pub trait AsyncNetworkClient { - fn send<'a>( - &'a mut self, - network_request: &'a NetworkRequest, - ) -> Pin>> + 'a>>; +pub trait NetworkClient { + fn send(&mut self, network_request: &NetworkRequest) -> impl Future>>; } diff --git a/crates/ironrdp-bench/Cargo.toml b/crates/ironrdp-bench/Cargo.toml index 3ab6a658..ea35d2ce 100644 --- a/crates/ironrdp-bench/Cargo.toml +++ b/crates/ironrdp-bench/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true publish = false [dev-dependencies] -criterion = "0.7" +criterion = "0.8" ironrdp-graphics.path = "../ironrdp-graphics" ironrdp-pdu.path = "../ironrdp-pdu" ironrdp-server = { path = "../ironrdp-server", features = ["__bench"] } diff --git a/crates/ironrdp-blocking/CHANGELOG.md b/crates/ironrdp-blocking/CHANGELOG.md index b02d867a..02048123 100644 --- a/crates/ironrdp-blocking/CHANGELOG.md +++ b/crates/ironrdp-blocking/CHANGELOG.md @@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.8.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-blocking-v0.7.0...ironrdp-blocking-v0.8.0)] - 2025-12-18 + +### Bug Fixes + +- [**breaking**] Use static dispatch for NetworkClient trait ([#1043](https://github.com/Devolutions/IronRDP/issues/1043)) ([bca6d190a8](https://github.com/Devolutions/IronRDP/commit/bca6d190a870708468534d224ff225a658767a9a)) + + - Rename `AsyncNetworkClient` to `NetworkClient` + - Replace dynamic dispatch (`Option<&mut dyn ...>`) with static dispatch + using generics (`&mut N where N: NetworkClient`) + - Reorder `connect_finalize` parameters for consistency across crates + ## [[0.4.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-blocking-v0.3.1...ironrdp-blocking-v0.4.0)] - 2025-03-12 ### Build @@ -13,7 +24,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Bump ironrdp-pdu - ## [[0.3.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-blocking-v0.3.0...ironrdp-blocking-v0.3.1)] - 2025-03-12 ### Build @@ -31,7 +41,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use CDN URLs instead of the blob storage URLs for Devolutions logo (#631) ([dd249909a8](https://github.com/Devolutions/IronRDP/commit/dd249909a894004d4f728d30b3a4aa77a0f8193b)) - ## [[0.2.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-blocking-v0.2.0...ironrdp-blocking-v0.2.1)] - 2024-12-14 ### Other diff --git a/crates/ironrdp-blocking/Cargo.toml b/crates/ironrdp-blocking/Cargo.toml index 3ce5774e..557640c8 100644 --- a/crates/ironrdp-blocking/Cargo.toml +++ b/crates/ironrdp-blocking/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-blocking" -version = "0.7.0" +version = "0.8.0" readme = "README.md" description = "Blocking I/O abstraction wrapping the IronRDP state machines conveniently" edition.workspace = true @@ -16,7 +16,7 @@ doctest = false test = false [dependencies] -ironrdp-connector = { path = "../ironrdp-connector", version = "0.7" } # public +ironrdp-connector = { path = "../ironrdp-connector", version = "0.8" } # public ironrdp-core = { path = "../ironrdp-core", version = "0.1", features = ["alloc"] } # public ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6" } # public tracing = { version = "0.1", features = ["log"] } diff --git a/crates/ironrdp-blocking/src/connector.rs b/crates/ironrdp-blocking/src/connector.rs index d601dd98..b50805ae 100644 --- a/crates/ironrdp-blocking/src/connector.rs +++ b/crates/ironrdp-blocking/src/connector.rs @@ -53,11 +53,11 @@ pub fn mark_as_upgraded(_: ShouldUpgrade, connector: &mut ClientConnector) -> Up #[instrument(skip_all)] pub fn connect_finalize( _: Upgraded, - framed: &mut Framed, mut connector: ClientConnector, + framed: &mut Framed, + network_client: &mut impl NetworkClient, server_name: ServerName, server_public_key: Vec, - network_client: &mut impl NetworkClient, kerberos_config: Option, ) -> ConnectorResult where @@ -69,12 +69,12 @@ where if connector.should_perform_credssp() { perform_credssp_step( - framed, &mut connector, + framed, + network_client, &mut buf, server_name, server_public_key, - network_client, kerberos_config, )?; } @@ -118,12 +118,12 @@ fn resolve_generator( #[instrument(level = "trace", skip_all)] fn perform_credssp_step( - framed: &mut Framed, connector: &mut ClientConnector, + framed: &mut Framed, + network_client: &mut impl NetworkClient, buf: &mut WriteBuf, server_name: ServerName, server_public_key: Vec, - network_client: &mut impl NetworkClient, kerberos_config: Option, ) -> ConnectorResult<()> where diff --git a/crates/ironrdp-client/Cargo.toml b/crates/ironrdp-client/Cargo.toml index fab17993..5a684444 100644 --- a/crates/ironrdp-client/Cargo.toml +++ b/crates/ironrdp-client/Cargo.toml @@ -32,7 +32,7 @@ qoiz = ["ironrdp/qoiz"] [dependencies] # Protocols -ironrdp = { path = "../ironrdp", version = "0.13", features = [ +ironrdp = { path = "../ironrdp", version = "0.14", features = [ "session", "input", "graphics", @@ -45,11 +45,11 @@ ironrdp = { path = "../ironrdp", version = "0.13", features = [ "connector", ] } ironrdp-core = { path = "../ironrdp-core", version = "0.1", features = ["alloc"] } -ironrdp-cliprdr-native = { path = "../ironrdp-cliprdr-native", version = "0.4" } +ironrdp-cliprdr-native = { path = "../ironrdp-cliprdr-native", version = "0.5" } ironrdp-rdpsnd-native = { path = "../ironrdp-rdpsnd-native", version = "0.4" } -ironrdp-tls = { path = "../ironrdp-tls", version = "0.1" } +ironrdp-tls = { path = "../ironrdp-tls", version = "0.2" } ironrdp-mstsgu = { path = "../ironrdp-mstsgu" } -ironrdp-tokio = { path = "../ironrdp-tokio", version = "0.7", features = ["reqwest"] } +ironrdp-tokio = { path = "../ironrdp-tokio", version = "0.8", features = ["reqwest"] } ironrdp-rdcleanpath.path = "../ironrdp-rdcleanpath" ironrdp-dvc-pipe-proxy.path = "../ironrdp-dvc-pipe-proxy" ironrdp-propertyset.path = "../ironrdp-propertyset" @@ -72,7 +72,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } # Async, futures tokio = { version = "1", features = ["full"] } tokio-util = { version = "0.7" } -tokio-tungstenite = "0.27" +tokio-tungstenite = "0.28" transport = { git = "https://github.com/Devolutions/devolutions-gateway", rev = "06e91dfe82751a6502eaf74b6a99663f06f0236d" } futures-util = { version = "0.3", features = ["sink"] } @@ -83,12 +83,12 @@ smallvec = "1.15" tap = "1" semver = "1" raw-window-handle = "0.6" -uuid = { version = "1.18" } +uuid = { version = "1.19" } x509-cert = { version = "0.2", default-features = false, features = ["std"] } url = "2" [target.'cfg(windows)'.dependencies] -windows = { version = "0.61", features = ["Win32_Foundation"] } +windows = { version = "0.62", features = ["Win32_Foundation"] } [lints] workspace = true diff --git a/crates/ironrdp-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..a6693fba 100644 --- a/crates/ironrdp-client/src/rdp.rs +++ b/crates/ironrdp-client/src/rdp.rs @@ -229,22 +229,24 @@ async fn connect( // Ensure there is no leftover let (initial_stream, leftover_bytes) = framed.into_inner(); - let (upgraded_stream, server_public_key) = ironrdp_tls::upgrade(initial_stream, config.destination.name()) + let (upgraded_stream, tls_cert) = ironrdp_tls::upgrade(initial_stream, config.destination.name()) .await .map_err(|e| connector::custom_err!("TLS upgrade", e))?; 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 server_public_key = ironrdp_tls::extract_tls_server_public_key(&tls_cert) + .ok_or_else(|| connector::general_err!("unable to extract tls server public key"))?; let connection_result = ironrdp_tokio::connect_finalize( upgraded, - &mut upgraded_framed, connector, + &mut upgraded_framed, + &mut ReqwestNetworkClient::new(), (&config.destination).into(), - server_public_key, - Some(&mut ReqwestNetworkClient::new()), + server_public_key.to_owned(), None, ) .await?; @@ -326,17 +328,17 @@ async fn connect_ws( let connection_result = ironrdp_tokio::connect_finalize( upgraded, - &mut framed, connector, + &mut framed, + &mut ReqwestNetworkClient::new(), (&config.destination).into(), server_public_key, - Some(&mut ReqwestNetworkClient::new()), None, ) .await?; 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 +662,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/CHANGELOG.md b/crates/ironrdp-cliprdr-native/CHANGELOG.md index 27b65067..20bbaa96 100644 --- a/crates/ironrdp-cliprdr-native/CHANGELOG.md +++ b/crates/ironrdp-cliprdr-native/CHANGELOG.md @@ -6,6 +6,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.5.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-cliprdr-native-v0.4.0...ironrdp-cliprdr-native-v0.5.0)] - 2025-12-18 + +### Bug Fixes + +- Prevent window class registration error on multiple sessions ([#1047](https://github.com/Devolutions/IronRDP/issues/1047)) ([a2af587e60](https://github.com/Devolutions/IronRDP/commit/a2af587e60e869f0235703e21772d1fc6a7dadcd)) + + When starting a second clipboard session, `RegisterClassA` would fail + with `ERROR_CLASS_ALREADY_EXISTS` because window classes are global to + the process. Now checks if the class is already registered before + attempting registration, allowing multiple WinClipboard instances to + coexist. + +### Build + +- Bump windows from 0.61.3 to 0.62.1 ([#1010](https://github.com/Devolutions/IronRDP/issues/1010)) ([79e71c4f90](https://github.com/Devolutions/IronRDP/commit/79e71c4f90ea68b14fe45241c1cf3953027b22a2)) + ## [[0.4.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-cliprdr-native-v0.3.0...ironrdp-cliprdr-native-v0.4.0)] - 2025-08-29 ### Bug Fixes diff --git a/crates/ironrdp-cliprdr-native/Cargo.toml b/crates/ironrdp-cliprdr-native/Cargo.toml index 9e338410..be193e66 100644 --- a/crates/ironrdp-cliprdr-native/Cargo.toml +++ b/crates/ironrdp-cliprdr-native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-cliprdr-native" -version = "0.4.0" +version = "0.5.0" readme = "README.md" description = "Native CLIPRDR static channel backend implementations for IronRDP" edition.workspace = true @@ -16,12 +16,12 @@ doctest = false test = false [dependencies] -ironrdp-cliprdr = { path = "../ironrdp-cliprdr", version = "0.4" } # public +ironrdp-cliprdr = { path = "../ironrdp-cliprdr", version = "0.5" } # public ironrdp-core = { path = "../ironrdp-core", version = "0.1" } tracing = { version = "0.1", features = ["log"] } [target.'cfg(windows)'.dependencies] -windows = { version = "0.61", features = [ +windows = { version = "0.62", features = [ "Win32_Foundation", "Win32_Graphics_Gdi", "Win32_System_DataExchange", diff --git a/crates/ironrdp-cliprdr-native/src/windows/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..87574ca5 100644 --- a/crates/ironrdp-cliprdr-native/src/windows/mod.rs +++ b/crates/ironrdp-cliprdr-native/src/windows/mod.rs @@ -19,7 +19,8 @@ use windows::Win32::System::DataExchange::{AddClipboardFormatListener, RemoveCli use windows::Win32::System::LibraryLoader::GetModuleHandleA; use windows::Win32::UI::Shell::{RemoveWindowSubclass, SetWindowSubclass}; use windows::Win32::UI::WindowsAndMessaging::{ - CreateWindowExA, DefWindowProcA, RegisterClassA, CW_USEDEFAULT, WINDOW_EX_STYLE, WM_USER, WNDCLASSA, WS_POPUP, + CreateWindowExA, DefWindowProcA, GetClassInfoA, RegisterClassA, CW_USEDEFAULT, WINDOW_EX_STYLE, WM_USER, WNDCLASSA, + WS_POPUP, }; use self::clipboard_impl::{clipboard_subproc, WinClipboardImpl}; @@ -152,17 +153,25 @@ impl WinClipboard { // SAFETY: low-level WinAPI call let instance = unsafe { GetModuleHandleA(None)? }; let window_class = s!("IronRDPClipboardMonitor"); - let wc = WNDCLASSA { - hInstance: instance.into(), - lpszClassName: window_class, - lpfnWndProc: Some(wndproc), - ..Default::default() - }; - // SAFETY: low-level WinAPI call - let atom = unsafe { RegisterClassA(&wc) }; - if atom == 0 { - return Err(WinCliprdrError::from(Error::from_win32())); + let mut existing_wc = WNDCLASSA::default(); + // SAFETY: `instance` is a valid module handle, `window_class` is a valid null-terminated string, + // and `existing_wc` is a valid mutable reference to a WNDCLASSA structure. + let class_exists = unsafe { GetClassInfoA(Some(instance.into()), window_class, &mut existing_wc).is_ok() }; + + if !class_exists { + let wc = WNDCLASSA { + hInstance: instance.into(), + lpszClassName: window_class, + lpfnWndProc: Some(wndproc), + ..Default::default() + }; + + // SAFETY: low-level WinAPI call + let atom = unsafe { RegisterClassA(&wc) }; + if atom == 0 { + return Err(WinCliprdrError::from(Error::from_thread())); + } } // SAFETY: low-level WinAPI call @@ -184,7 +193,7 @@ impl WinClipboard { }; if window.is_invalid() { - return Err(WinCliprdrError::from(Error::from_win32())); + return Err(WinCliprdrError::from(Error::from_thread())); } // Init clipboard processing for WinAPI event loop // @@ -200,8 +209,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/CHANGELOG.md b/crates/ironrdp-cliprdr/CHANGELOG.md index d96b4760..c9653f15 100644 --- a/crates/ironrdp-cliprdr/CHANGELOG.md +++ b/crates/ironrdp-cliprdr/CHANGELOG.md @@ -6,6 +6,30 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.5.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-cliprdr-v0.4.0...ironrdp-cliprdr-v0.5.0)] - 2025-12-18 + +### Bug Fixes + +- Fixes the Cliprdr `SvcProcessor` impl to support handling a `TemporaryDirectory` Clipboard PDU ([#1031](https://github.com/Devolutions/IronRDP/issues/1031)) ([f2326ef046](https://github.com/Devolutions/IronRDP/commit/f2326ef046cc81fb0e8985f03382859085882e86)) + +- Allow servers to announce clipboard ownership ([#1053](https://github.com/Devolutions/IronRDP/issues/1053)) ([d587b0c4c1](https://github.com/Devolutions/IronRDP/commit/d587b0c4c114c49d30f52859f43b22f829456a01)) + + Servers can now send Format List PDU via initiate_copy() regardless of + internal state. The existing state machine was designed for clients + where clipboard initialization must complete before announcing + ownership. + + MS-RDPECLIP Section 2.2.3.1 specifies that Format List PDU is sent by + either client or server when the local clipboard is updated. Servers + should be able to announce clipboard changes immediately after channel + negotiation. + + This change enables RDP servers to properly announce clipboard ownership + by bypassing the Initialization/Ready state check when R::is_server() is + true. Client behavior remains unchanged. + +- [**breaking**] Removed the `PackedMetafile::data()` method in favor of making the `PackedMetafile::data` field public. + ## [[0.4.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-cliprdr-v0.3.0...ironrdp-cliprdr-v0.4.0)] - 2025-08-29 ### Bug Fixes diff --git a/crates/ironrdp-cliprdr/Cargo.toml b/crates/ironrdp-cliprdr/Cargo.toml index e02cfefd..3dd216da 100644 --- a/crates/ironrdp-cliprdr/Cargo.toml +++ b/crates/ironrdp-cliprdr/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-cliprdr" -version = "0.4.0" +version = "0.5.0" readme = "README.md" description = "CLIPRDR static channel for clipboard implemented as described in MS-RDPECLIP" edition.workspace = true diff --git a/crates/ironrdp-cliprdr/src/lib.rs b/crates/ironrdp-cliprdr/src/lib.rs index fa83f1ab..41f5d62c 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; @@ -32,16 +28,12 @@ pub type CliprdrSvcMessages = SvcProcessorMessages>; #[derive(Debug)] enum ClipboardError { - UnimplementedPdu { pdu: &'static str }, FormatListRejected, } impl core::fmt::Display for ClipboardError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - ClipboardError::UnimplementedPdu { pdu } => { - write!(f, "received clipboard PDU `{pdu}` is not implemented") - } ClipboardError::FormatListRejected => write!(f, "sent format list was rejected"), } } @@ -238,26 +230,32 @@ impl Cliprdr { pub fn initiate_copy(&self, available_formats: &[ClipboardFormat]) -> PduResult> { let mut pdus = Vec::new(); - match (self.state, R::is_server()) { - // When user initiates copy, we should send format list to server. - (CliprdrState::Ready, _) => { - pdus.push(ClipboardPdu::FormatList( - self.build_format_list(available_formats).map_err(|e| encode_err!(e))?, - )); - } - (CliprdrState::Initialization, false) => { - // During initialization state, first copy action is synthetic and should be sent along with - // capabilities and temporary directory PDUs. - pdus.push(ClipboardPdu::Capabilities(self.capabilities.clone())); - pdus.push(ClipboardPdu::TemporaryDirectory( - ClientTemporaryDirectory::new(self.backend.temporary_directory()).map_err(|e| encode_err!(e))?, - )); - pdus.push(ClipboardPdu::FormatList( - self.build_format_list(available_formats).map_err(|e| encode_err!(e))?, - )); - } - _ => { - error!(?self.state, "Attempted to initiate copy in incorrect state"); + if R::is_server() { + pdus.push(ClipboardPdu::FormatList( + self.build_format_list(available_formats).map_err(|e| encode_err!(e))?, + )); + } else { + match self.state { + CliprdrState::Ready => { + pdus.push(ClipboardPdu::FormatList( + self.build_format_list(available_formats).map_err(|e| encode_err!(e))?, + )); + } + CliprdrState::Initialization => { + // During initialization state, first copy action is synthetic and should be sent along with + // capabilities and temporary directory PDUs. + pdus.push(ClipboardPdu::Capabilities(self.capabilities.clone())); + pdus.push(ClipboardPdu::TemporaryDirectory( + ClientTemporaryDirectory::new(self.backend.temporary_directory()) + .map_err(|e| encode_err!(e))?, + )); + pdus.push(ClipboardPdu::FormatList( + self.build_format_list(available_formats).map_err(|e| encode_err!(e))?, + )); + } + _ => { + error!(?self.state, "Attempted to initiate copy in incorrect state"); + } } } @@ -337,9 +335,10 @@ impl SvcProcessor for Cliprdr { self.backend.on_file_contents_response(response); Ok(Vec::new()) } - _ => self.handle_error_transition(ClipboardError::UnimplementedPdu { - pdu: pdu.message_name(), - }), + ClipboardPdu::TemporaryDirectory(_) => { + // do nothing + Ok(Vec::new()) + } } } 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/CHANGELOG.md b/crates/ironrdp-connector/CHANGELOG.md index 6fe86b40..888acabe 100644 --- a/crates/ironrdp-connector/CHANGELOG.md +++ b/crates/ironrdp-connector/CHANGELOG.md @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.8.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-connector-v0.7.1...ironrdp-connector-v0.8.0)] - 2025-12-18 + +### Build + +- Bump picky and sspi ([#1028](https://github.com/Devolutions/IronRDP/issues/1028)) ([5bd319126d](https://github.com/Devolutions/IronRDP/commit/5bd319126d32fbd8e505508e27ab2b1a18a83d04)) + + This fixes build issues with some dependencies. + ## [[0.7.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-connector-v0.7.0...ironrdp-connector-v0.7.1)] - 2025-09-04 ### Features diff --git a/crates/ironrdp-connector/Cargo.toml b/crates/ironrdp-connector/Cargo.toml index 2ff693fb..8119bab4 100644 --- a/crates/ironrdp-connector/Cargo.toml +++ b/crates/ironrdp-connector/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-connector" -version = "0.7.1" +version = "0.8.0" readme = "README.md" description = "State machines to drive an RDP connection sequence" edition.workspace = true @@ -27,13 +27,13 @@ ironrdp-core = { path = "../ironrdp-core", version = "0.1" } # public ironrdp-error = { path = "../ironrdp-error", version = "0.1" } # public ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6", features = ["std"] } # public arbitrary = { version = "1", features = ["derive"], optional = true } # public -sspi = "0.16" # public +sspi = { version = "0.18", features = ["scard"] } url = "2.5" # public rand = { version = "0.9", features = ["std"] } # TODO: dependency injection? tracing = { version = "0.1", features = ["log"] } picky-asn1-der = "0.5" -picky-asn1-x509 = "0.14" -picky = "7.0.0-rc.17" +picky-asn1-x509 = "0.15" +picky = "=7.0.0-rc.20" # FIXME: We are pinning with = because the candidate version number counts as the minor number by Cargo, and will be automatically bumped in the Cargo.lock. [lints] workspace = true diff --git a/crates/ironrdp-connector/src/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/credssp.rs b/crates/ironrdp-connector/src/credssp.rs index 9750b608..866ac508 100644 --- a/crates/ironrdp-connector/src/credssp.rs +++ b/crates/ironrdp-connector/src/credssp.rs @@ -5,6 +5,7 @@ use picky_asn1_x509::{oids, Certificate, ExtensionView, GeneralName}; use sspi::credssp::{self, ClientState, CredSspClient}; use sspi::generator::{Generator, NetworkRequest}; use sspi::negotiate::ProtocolConfig; +use sspi::Secret; use sspi::Username; use tracing::debug; @@ -123,11 +124,13 @@ impl CredsspSequence { certificate: cert, reader_name: config.reader_name.clone(), card_name: None, - container_name: config.container_name.clone(), + container_name: Some(config.container_name.clone()), csp_name: config.csp_name.clone(), pin: pin.as_bytes().to_vec().into(), - private_key_file_index: None, private_key: Some(key.into()), + scard_type: sspi::SmartCardType::Emulated { + scard_pin: Secret::new(pin.as_bytes().to_vec()), + }, }; sspi::Credentials::SmartCard(Box::new(identity)) } diff --git a/crates/ironrdp-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-futures/CHANGELOG.md b/crates/ironrdp-futures/CHANGELOG.md index d0173f76..3a35de72 100644 --- a/crates/ironrdp-futures/CHANGELOG.md +++ b/crates/ironrdp-futures/CHANGELOG.md @@ -6,13 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.6.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-futures-v0.5.0...ironrdp-futures-v0.6.0)] - 2025-12-18 + + ## [[0.1.3](https://github.com/Devolutions/IronRDP/compare/ironrdp-futures-v0.1.2...ironrdp-futures-v0.1.3)] - 2025-03-12 ### Build - Update dependencies (#695) ([c21fa44fd6](https://github.com/Devolutions/IronRDP/commit/c21fa44fd6f3c6a6b74788ff68e83133c1314caa)) - ## [[0.1.2](https://github.com/Devolutions/IronRDP/compare/ironrdp-futures-v0.1.1...ironrdp-futures-v0.1.2)] - 2025-01-28 ### Documentation diff --git a/crates/ironrdp-futures/Cargo.toml b/crates/ironrdp-futures/Cargo.toml index aa8e67ec..ed75e138 100644 --- a/crates/ironrdp-futures/Cargo.toml +++ b/crates/ironrdp-futures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-futures" -version = "0.5.0" +version = "0.6.0" readme = "README.md" description = "`Framed*` traits implementation above futures’s traits" edition.workspace = true @@ -17,7 +17,7 @@ test = false [dependencies] futures-util = { version = "0.3", features = ["io"] } # public -ironrdp-async = { path = "../ironrdp-async", version = "0.7" } # public +ironrdp-async = { path = "../ironrdp-async", version = "0.8" } # public bytes = "1" # public [lints] diff --git a/crates/ironrdp-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/CHANGELOG.md b/crates/ironrdp-graphics/CHANGELOG.md index 41609a94..1a7bfd3c 100644 --- a/crates/ironrdp-graphics/CHANGELOG.md +++ b/crates/ironrdp-graphics/CHANGELOG.md @@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.7.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-graphics-v0.6.0...ironrdp-graphics-v0.7.0)] - 2025-12-18 + +### Added + +- [**breaking**] `InvalidIntegralConversion` variant in `RlgrError` and `ZgfxError` + +### Build + +- Bump bytemuck from 1.23.2 to 1.24.0 ([#1008](https://github.com/Devolutions/IronRDP/issues/1008)) ([a24a1fa9e8](https://github.com/Devolutions/IronRDP/commit/a24a1fa9e8f1898b2fcdd41d87660ab9e38f89ed)) + ## [[0.6.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-graphics-v0.5.0...ironrdp-graphics-v0.6.0)] - 2025-06-27 ### Bug Fixes diff --git a/crates/ironrdp-graphics/Cargo.toml b/crates/ironrdp-graphics/Cargo.toml index 43e4b5e5..938612c1 100644 --- a/crates/ironrdp-graphics/Cargo.toml +++ b/crates/ironrdp-graphics/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-graphics" -version = "0.6.0" +version = "0.7.0" readme = "README.md" description = "RDP image processing primitives" edition.workspace = true @@ -22,14 +22,13 @@ 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"] } [dev-dependencies] bmp = "0.5" -bytemuck = "1.23" +bytemuck = "1.24" expect-test.workspace = true [lints] 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/Cargo.toml b/crates/ironrdp-mstsgu/Cargo.toml index fdd83edb..b194f467 100644 --- a/crates/ironrdp-mstsgu/Cargo.toml +++ b/crates/ironrdp-mstsgu/Cargo.toml @@ -30,12 +30,12 @@ hyper-util = { version = "0.1", features = ["tokio"] } hyper = { version = "1.7", features = ["client", "http1"] } ironrdp-core = { path = "../ironrdp-core", version = "0.1", features = ["std"] } ironrdp-error = { path = "../ironrdp-error", version = "0.1" } -ironrdp-tls = { path = "../ironrdp-tls", version = "0.1" } +ironrdp-tls = { path = "../ironrdp-tls", version = "0.2" } log = "0.4" -tokio-tungstenite = { version = "0.27" } +tokio-tungstenite = { version = "0.28" } tokio-util = { version = "0.7" } tokio = { version = "1.43", features = ["macros", "rt"] } -uuid = { version = "1.16", features = ["v4"] } +uuid = { version = "1.19", features = ["v4"] } [lints] workspace = true 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-native/CHANGELOG.md b/crates/ironrdp-rdpdr-native/CHANGELOG.md index 4370951c..301fa404 100644 --- a/crates/ironrdp-rdpdr-native/CHANGELOG.md +++ b/crates/ironrdp-rdpdr-native/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.5.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpdr-native-v0.4.0...ironrdp-rdpdr-native-v0.5.0)] - 2025-12-18 + + ## [[0.4.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpdr-native-v0.3.0...ironrdp-rdpdr-native-v0.4.0)] - 2025-08-29 ### Build diff --git a/crates/ironrdp-rdpdr-native/Cargo.toml b/crates/ironrdp-rdpdr-native/Cargo.toml index 1d61a701..14fea584 100644 --- a/crates/ironrdp-rdpdr-native/Cargo.toml +++ b/crates/ironrdp-rdpdr-native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-rdpdr-native" -version = "0.4.0" +version = "0.5.0" readme = "README.md" description = "Native RDPDR static channel backend implementations for IronRDP" edition.workspace = true @@ -19,6 +19,6 @@ test = false ironrdp-core = { path = "../ironrdp-core", version = "0.1" } ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6" } # public ironrdp-svc = { path = "../ironrdp-svc", version = "0.5" } # public -ironrdp-rdpdr = { path = "../ironrdp-rdpdr", version = "0.4" } # public +ironrdp-rdpdr = { path = "../ironrdp-rdpdr", version = "0.5" } # public nix = { version = "0.30", features = ["fs", "dir"] } tracing = { version = "0.1", features = ["log"] } diff --git a/crates/ironrdp-rdpdr/CHANGELOG.md b/crates/ironrdp-rdpdr/CHANGELOG.md index 99da423d..e61a8251 100644 --- a/crates/ironrdp-rdpdr/CHANGELOG.md +++ b/crates/ironrdp-rdpdr/CHANGELOG.md @@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.5.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpdr-v0.4.1...ironrdp-rdpdr-v0.5.0)] - 2025-12-18 + +### Bug Fixes + +- Fix incorrect padding when parsing NDR strings ([#1015](https://github.com/Devolutions/IronRDP/issues/1015)) ([a0a3e750c9](https://github.com/Devolutions/IronRDP/commit/a0a3e750c9e4ee9c73b957fbcb26dbc59e57d07d)) + + When parsing Network Data Representation (NDR) messages, we're supposed + to account for padding at the end of strings to remain aligned on a + 4-byte boundary. The existing code doesn't seem to cover all cases, and + the resulting misalignment causes misleading errors when processing the + rest of the message. + ## [[0.4.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpdr-v0.4.0...ironrdp-rdpdr-v0.4.1)] - 2025-09-04 ### Features @@ -18,29 +30,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add USER_LOGGEDON flag support ([5e78f91713](https://github.com/Devolutions/IronRDP/commit/5e78f917132a174bdd5d8711beb1744de1bd265a)) - ## [[0.2.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpdr-v0.1.3...ironrdp-rdpdr-v0.2.0)] - 2025-03-12 ### Build - Bump ironrdp-pdu - ## [[0.1.3](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpdr-v0.1.2...ironrdp-rdpdr-v0.1.3)] - 2025-03-12 ### Build - Update dependencies (#695) ([c21fa44fd6](https://github.com/Devolutions/IronRDP/commit/c21fa44fd6f3c6a6b74788ff68e83133c1314caa)) - ## [[0.1.2](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpdr-v0.1.1...ironrdp-rdpdr-v0.1.2)] - 2025-01-28 ### Documentation - Use CDN URLs instead of the blob storage URLs for Devolutions logo (#631) ([dd249909a8](https://github.com/Devolutions/IronRDP/commit/dd249909a894004d4f728d30b3a4aa77a0f8193b)) - - ## [[0.1.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpdr-v0.1.0...ironrdp-rdpdr-v0.1.1)] - 2024-12-14 ### Other diff --git a/crates/ironrdp-rdpdr/Cargo.toml b/crates/ironrdp-rdpdr/Cargo.toml index 32a762e7..b585bff1 100644 --- a/crates/ironrdp-rdpdr/Cargo.toml +++ b/crates/ironrdp-rdpdr/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-rdpdr" -version = "0.4.1" +version = "0.5.0" readme = "README.md" description = "RDPDR channel implementation." edition.workspace = true diff --git a/crates/ironrdp-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/CHANGELOG.md b/crates/ironrdp-rdpsnd-native/CHANGELOG.md index 9b3c6938..ff009f99 100644 --- a/crates/ironrdp-rdpsnd-native/CHANGELOG.md +++ b/crates/ironrdp-rdpsnd-native/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.4.2](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpsnd-native-v0.4.1...ironrdp-rdpsnd-native-v0.4.2)] - 2025-12-18 + +### Build + +- Bump bytemuck from 1.23.2 to 1.24.0 ([#1008](https://github.com/Devolutions/IronRDP/issues/1008)) ([a24a1fa9e8](https://github.com/Devolutions/IronRDP/commit/a24a1fa9e8f1898b2fcdd41d87660ab9e38f89ed)) + ## [[0.4.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpsnd-native-v0.4.0...ironrdp-rdpsnd-native-v0.4.1)] - 2025-09-24 ### Build @@ -49,7 +55,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use CDN URLs instead of the blob storage URLs for Devolutions logo (#631) ([dd249909a8](https://github.com/Devolutions/IronRDP/commit/dd249909a894004d4f728d30b3a4aa77a0f8193b)) - ## [[0.1.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-rdpsnd-native-v0.1.0...ironrdp-rdpsnd-native-v0.1.1)] - 2024-12-15 ### Other diff --git a/crates/ironrdp-rdpsnd-native/Cargo.toml b/crates/ironrdp-rdpsnd-native/Cargo.toml index 26683271..c6711eb0 100644 --- a/crates/ironrdp-rdpsnd-native/Cargo.toml +++ b/crates/ironrdp-rdpsnd-native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-rdpsnd-native" -version = "0.4.1" +version = "0.4.2" description = "Native RDPSND static channel backend implementations for IronRDP" edition.workspace = true license.workspace = true @@ -20,7 +20,7 @@ opus = ["dep:opus2", "dep:bytemuck"] [dependencies] anyhow = "1" -bytemuck = { version = "1.23", optional = true } +bytemuck = { version = "1.24", optional = true } cpal = "0.16" ironrdp-rdpsnd = { path = "../ironrdp-rdpsnd", version = "0.6" } # public opus2 = { version = "0.3", optional = true, features = ["bundled"] } 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/CHANGELOG.md b/crates/ironrdp-server/CHANGELOG.md index e68c17d2..916740d6 100644 --- a/crates/ironrdp-server/CHANGELOG.md +++ b/crates/ironrdp-server/CHANGELOG.md @@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.10.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-server-v0.9.0...ironrdp-server-v0.10.0)] - 2025-12-18 + +### Bug Fixes + +- Send TLS close_notify during graceful RDP disconnect ([#1032](https://github.com/Devolutions/IronRDP/issues/1032)) ([a70e01d9c5](https://github.com/Devolutions/IronRDP/commit/a70e01d9c5675a7dffd65eda7428537c8ad6a857)) + + Add support for sending a proper TLS close_notify message when the RDP + client initiates a graceful disconnect PDU. + ## [[0.9.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-server-v0.8.0...ironrdp-server-v0.9.0)] - 2025-09-24 ### Bug Fixes diff --git a/crates/ironrdp-server/Cargo.toml b/crates/ironrdp-server/Cargo.toml index e4d531d3..b1e4650c 100644 --- a/crates/ironrdp-server/Cargo.toml +++ b/crates/ironrdp-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-server" -version = "0.9.0" +version = "0.10.0" readme = "README.md" description = "Extendable skeleton for implementing custom RDP servers" edition.workspace = true @@ -31,17 +31,17 @@ anyhow = "1.0" tokio = { version = "1", features = ["net", "macros", "sync", "rt"] } # public tokio-rustls = "0.26" # public async-trait = "0.1" -ironrdp-async = { path = "../ironrdp-async", version = "0.7" } +ironrdp-async = { path = "../ironrdp-async", version = "0.8" } ironrdp-ainput = { path = "../ironrdp-ainput", version = "0.4" } ironrdp-core = { path = "../ironrdp-core", version = "0.1" } ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6" } # public ironrdp-svc = { path = "../ironrdp-svc", version = "0.5" } # public -ironrdp-cliprdr = { path = "../ironrdp-cliprdr", version = "0.4" } # public +ironrdp-cliprdr = { path = "../ironrdp-cliprdr", version = "0.5" } # public ironrdp-displaycontrol = { path = "../ironrdp-displaycontrol", version = "0.4" } # public ironrdp-dvc = { path = "../ironrdp-dvc", version = "0.4" } # public -ironrdp-tokio = { path = "../ironrdp-tokio", version = "0.7" } -ironrdp-acceptor = { path = "../ironrdp-acceptor", version = "0.7" } # public -ironrdp-graphics = { path = "../ironrdp-graphics", version = "0.6" } # public +ironrdp-tokio = { path = "../ironrdp-tokio", version = "0.8" } +ironrdp-acceptor = { path = "../ironrdp-acceptor", version = "0.8" } # public +ironrdp-graphics = { path = "../ironrdp-graphics", version = "0.7" } # public ironrdp-rdpsnd = { path = "../ironrdp-rdpsnd", version = "0.6" } # public tracing = { version = "0.1", features = ["log"] } x509-cert = { version = "0.2.5", optional = true } diff --git a/crates/ironrdp-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-server/src/server.rs b/crates/ironrdp-server/src/server.rs index 9023a4cc..9fe234d8 100644 --- a/crates/ironrdp-server/src/server.rs +++ b/crates/ironrdp-server/src/server.rs @@ -21,7 +21,7 @@ use ironrdp_pdu::{decode_err, mcs, nego, rdp, Action, PduResult}; use ironrdp_svc::{server_encode_svc_messages, StaticChannelId, StaticChannelSet, SvcProcessor}; use ironrdp_tokio::{split_tokio_framed, unsplit_tokio_framed, FramedRead, FramedWrite, TokioFramed}; use rdpsnd::server::{RdpsndServer, RdpsndServerMessage}; -use tokio::io::{AsyncRead, AsyncWrite}; +use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt as _}; use tokio::net::{TcpListener, TcpStream}; use tokio::sync::{mpsc, oneshot, Mutex}; use tokio::task; @@ -349,15 +349,20 @@ impl RdpServer { ironrdp_acceptor::accept_credssp( &mut framed, &mut acceptor, + &mut ironrdp_tokio::reqwest::ReqwestNetworkClient::new(), client_name.into(), pub_key.clone(), None, - None, ) .await?; } - self.accept_finalize(framed, acceptor).await?; + let framed = self.accept_finalize(framed, acceptor).await?; + debug!("Shutting down TLS connection"); + let (mut tls_stream, _) = framed.into_inner(); + if let Err(e) = tls_stream.shutdown().await { + debug!(?e, "TLS shutdown error"); + } } BeginResult::Continue(framed) => { @@ -954,7 +959,7 @@ impl RdpServer { } } - async fn accept_finalize(&mut self, mut framed: TokioFramed, mut acceptor: Acceptor) -> Result<()> + async fn accept_finalize(&mut self, mut framed: TokioFramed, mut acceptor: Acceptor) -> Result> where S: AsyncRead + AsyncWrite + Sync + Send + Unpin, { @@ -982,11 +987,12 @@ impl RdpServer { framed = unsplit_tokio_framed(reader, writer); continue; } - RunState::Disconnect => break, + RunState::Disconnect => { + let final_framed = unsplit_tokio_framed(reader, writer); + return Ok(final_framed); + } } } - - Ok(()) } pub fn set_credentials(&mut self, creds: Option) { diff --git a/crates/ironrdp-session/CHANGELOG.md b/crates/ironrdp-session/CHANGELOG.md index 3b043327..90011784 100644 --- a/crates/ironrdp-session/CHANGELOG.md +++ b/crates/ironrdp-session/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.8.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-session-v0.7.0...ironrdp-session-v0.8.0)] - 2025-12-18 + + ## [[0.6.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-session-v0.5.0...ironrdp-session-v0.6.0)] - 2025-08-29 ### Features diff --git a/crates/ironrdp-session/Cargo.toml b/crates/ironrdp-session/Cargo.toml index 1d9436aa..dd00422d 100644 --- a/crates/ironrdp-session/Cargo.toml +++ b/crates/ironrdp-session/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-session" -version = "0.7.0" +version = "0.8.0" readme = "README.md" description = "State machines to drive an RDP session" edition.workspace = true @@ -22,11 +22,11 @@ qoiz = ["dep:zstd-safe", "qoi"] [dependencies] ironrdp-core = { path = "../ironrdp-core", version = "0.1" } # public -ironrdp-connector = { path = "../ironrdp-connector", version = "0.7" } # public # TODO: at some point, this dependency could be removed (good for compilation speed) +ironrdp-connector = { path = "../ironrdp-connector", version = "0.8" } # public # TODO: at some point, this dependency could be removed (good for compilation speed) ironrdp-svc = { path = "../ironrdp-svc", version = "0.5" } # public ironrdp-dvc = { path = "../ironrdp-dvc", version = "0.4" } # public ironrdp-error = { path = "../ironrdp-error", version = "0.1" } # public -ironrdp-graphics = { path = "../ironrdp-graphics", version = "0.6" } # public +ironrdp-graphics = { path = "../ironrdp-graphics", version = "0.7" } # public ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6", features = ["std"] } # public ironrdp-displaycontrol = { path = "../ironrdp-displaycontrol", version = "0.4" } tracing = { version = "0.1", features = ["log"] } diff --git a/crates/ironrdp-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 96% rename from crates/ironrdp-testsuite-extra/tests/tests/mod.rs rename to crates/ironrdp-testsuite-extra/tests/mod.rs index 7127fa5f..8cc24fab 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. @@ -205,18 +205,20 @@ where .await .expect("begin connection"); let initial_stream = framed.into_inner_no_leftover(); - let (upgraded_stream, server_public_key) = ironrdp_tls::upgrade(initial_stream, "localhost") + let (upgraded_stream, tls_cert) = ironrdp_tls::upgrade(initial_stream, "localhost") .await .expect("TLS upgrade"); let upgraded = ironrdp_tokio::mark_as_upgraded(should_upgrade, &mut connector); let mut upgraded_framed = ironrdp_tokio::TokioFramed::new(upgraded_stream); + let server_public_key = + ironrdp_tls::extract_tls_server_public_key(&tls_cert).expect("extract server public key"); let connection_result = ironrdp_async::connect_finalize( upgraded, - &mut upgraded_framed, connector, + &mut upgraded_framed, + &mut ironrdp_tokio::reqwest::ReqwestNetworkClient::new(), "localhost".into(), - server_public_key, - None, + server_public_key.to_owned(), None, ) .await diff --git a/crates/ironrdp-tls/CHANGELOG.md b/crates/ironrdp-tls/CHANGELOG.md index 455742e7..2aaa1c25 100644 --- a/crates/ironrdp-tls/CHANGELOG.md +++ b/crates/ironrdp-tls/CHANGELOG.md @@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.2.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-tls-v0.1.4...ironrdp-tls-v0.2.0)] - 2025-12-18 + +### Features + +- [**breaking**] Return x509_cert::Certificate from upgrade() ([#1054](https://github.com/Devolutions/IronRDP/issues/1054)) ([bd2aed7686](https://github.com/Devolutions/IronRDP/commit/bd2aed76867f4038c32df9a0d24532ee40d2f14c)) + + This allows client applications to verify details of the certificate, + possibly with the user, when connecting to a server using TLS. + ## [[0.1.4](https://github.com/Devolutions/IronRDP/compare/ironrdp-tls-v0.1.3...ironrdp-tls-v0.1.4)] - 2025-08-29 ### Build diff --git a/crates/ironrdp-tls/Cargo.toml b/crates/ironrdp-tls/Cargo.toml index 55556ffd..1c719be0 100644 --- a/crates/ironrdp-tls/Cargo.toml +++ b/crates/ironrdp-tls/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-tls" -version = "0.1.4" +version = "0.2.0" readme = "README.md" description = "TLS boilerplate common with most IronRDP clients" edition.workspace = true @@ -23,7 +23,7 @@ stub = [] [dependencies] tokio = { version = "1.47" } -x509-cert = { version = "0.2", default-features = false, features = ["std"], optional = true } +x509-cert = { version = "0.2", default-features = false, features = ["std"], optional = true } # public tokio-native-tls = { version = "0.3", optional = true } # public tokio-rustls = { version = "0.26", optional = true } # public diff --git a/crates/ironrdp-tls/src/lib.rs b/crates/ironrdp-tls/src/lib.rs index 63261890..58349712 100644 --- a/crates/ironrdp-tls/src/lib.rs +++ b/crates/ironrdp-tls/src/lib.rs @@ -25,21 +25,9 @@ compile_error!("a TLS backend must be selected by enabling a single feature out #[cfg(any(feature = "stub", feature = "native-tls", feature = "rustls"))] pub use impl_::{upgrade, TlsStream}; -#[cfg(any(feature = "native-tls", feature = "rustls"))] -pub(crate) fn extract_tls_server_public_key(cert: &[u8]) -> std::io::Result> { - use std::io; - - use x509_cert::der::Decode as _; - - let cert = x509_cert::Certificate::from_der(cert).map_err(io::Error::other)?; - - let server_public_key = cert - .tbs_certificate +pub fn extract_tls_server_public_key(cert: &x509_cert::Certificate) -> Option<&[u8]> { + cert.tbs_certificate .subject_public_key_info .subject_public_key .as_bytes() - .ok_or_else(|| io::Error::other("subject public key BIT STRING is not aligned"))? - .to_owned(); - - Ok(server_public_key) } diff --git a/crates/ironrdp-tls/src/native_tls.rs b/crates/ironrdp-tls/src/native_tls.rs index 0e652e7e..f3b7d0d0 100644 --- a/crates/ironrdp-tls/src/native_tls.rs +++ b/crates/ironrdp-tls/src/native_tls.rs @@ -4,7 +4,7 @@ use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt as _}; pub type TlsStream = tokio_native_tls::TlsStream; -pub async fn upgrade(stream: S, server_name: &str) -> io::Result<(TlsStream, Vec)> +pub async fn upgrade(stream: S, server_name: &str) -> io::Result<(TlsStream, x509_cert::Certificate)> where S: Unpin + AsyncRead + AsyncWrite, { @@ -24,15 +24,18 @@ where tls_stream.flush().await?; - let server_public_key = { + let tls_cert = { + use x509_cert::der::Decode as _; + let cert = tls_stream .get_ref() .peer_certificate() .map_err(|e| io::Error::new(io::ErrorKind::Other, e))? .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "peer certificate is missing"))?; let cert = cert.to_der().map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; - crate::extract_tls_server_public_key(&cert)? + + x509_cert::Certificate::from_der(&cert).map_err(io::Error::other)? }; - Ok((tls_stream, server_public_key)) + Ok((tls_stream, tls_cert)) } diff --git a/crates/ironrdp-tls/src/rustls.rs b/crates/ironrdp-tls/src/rustls.rs index ea3aa3c0..ca8778d5 100644 --- a/crates/ironrdp-tls/src/rustls.rs +++ b/crates/ironrdp-tls/src/rustls.rs @@ -6,7 +6,7 @@ use tokio_rustls::rustls::pki_types::ServerName; pub type TlsStream = tokio_rustls::client::TlsStream; -pub async fn upgrade(stream: S, server_name: &str) -> io::Result<(TlsStream, Vec)> +pub async fn upgrade(stream: S, server_name: &str) -> io::Result<(TlsStream, x509_cert::Certificate)> where S: Unpin + AsyncRead + AsyncWrite, { @@ -35,17 +35,20 @@ where tls_stream.flush().await?; - let server_public_key = { + let tls_cert = { + use x509_cert::der::Decode as _; + let cert = tls_stream .get_ref() .1 .peer_certificates() .and_then(|certificates| certificates.first()) .ok_or_else(|| io::Error::other("peer certificate is missing"))?; - crate::extract_tls_server_public_key(cert)? + + x509_cert::Certificate::from_der(cert).map_err(io::Error::other)? }; - Ok((tls_stream, server_public_key)) + Ok((tls_stream, tls_cert)) } mod danger { diff --git a/crates/ironrdp-tokio/CHANGELOG.md b/crates/ironrdp-tokio/CHANGELOG.md index 4265c72b..db3bd881 100644 --- a/crates/ironrdp-tokio/CHANGELOG.md +++ b/crates/ironrdp-tokio/CHANGELOG.md @@ -6,6 +6,49 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.8.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-tokio-v0.7.0...ironrdp-tokio-v0.8.0)] - 2025-12-18 + +### Features + +- Add MovableTokioFramed for Send+!Sync context ([#1033](https://github.com/Devolutions/IronRDP/issues/1033)) ([966ba8a53e](https://github.com/Devolutions/IronRDP/commit/966ba8a53e43a193271f40b9db80e45e495e2f24)) + + The `ironrdp-tokio` crate currently provides the following two + `Framed` implementations using the standard `tokio::io` traits: + - `type TokioFramed = Framed>` where `S: Send + Sync + + Unpin` + - `type LocalTokioFramed = Framed>` where `S: + Unpin` + + The former is meant for multi-threaded runtimes and the latter is meant + for single-threaded runtimes. + + This PR adds a third `Framed` implementation: + + `pub type MovableTokioFramed = Framed>` where + `S: Send + Unpin` + + This is a valid usecase as some implementations of the `tokio::io` + traits are `Send` but `!Sync`. Without this new third type, consumers of + `Framed` who have a `S: Send + !Sync` trait for their streams are + forced to downgrade to `LocalTokioFramed` and do some hacky workaround + with `tokio::task::spawn_blocking` since the defined associated futures, + `ReadFut` and `WriteAllFut`, are neither `Send` nor `Sync`. + +### Bug Fixes + +- [**breaking**] Use static dispatch for NetworkClient trait ([#1043](https://github.com/Devolutions/IronRDP/issues/1043)) ([bca6d190a8](https://github.com/Devolutions/IronRDP/commit/bca6d190a870708468534d224ff225a658767a9a)) + + - Rename `AsyncNetworkClient` to `NetworkClient` + - Replace dynamic dispatch (`Option<&mut dyn ...>`) with static dispatch + using generics (`&mut N where N: NetworkClient`) + - Reorder `connect_finalize` parameters for consistency across crates + +### Build + +- Bump picky and sspi ([#1028](https://github.com/Devolutions/IronRDP/issues/1028)) ([5bd319126d](https://github.com/Devolutions/IronRDP/commit/5bd319126d32fbd8e505508e27ab2b1a18a83d04)) + + This fixes build issues with some dependencies. + ## [[0.6.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-tokio-v0.5.1...ironrdp-tokio-v0.6.0)] - 2025-07-08 ### Build diff --git a/crates/ironrdp-tokio/Cargo.toml b/crates/ironrdp-tokio/Cargo.toml index e129ac44..abb08162 100644 --- a/crates/ironrdp-tokio/Cargo.toml +++ b/crates/ironrdp-tokio/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp-tokio" -version = "0.7.0" +version = "0.8.0" readme = "README.md" description = "`Framed*` traits implementation above Tokio’s traits" edition.workspace = true @@ -23,11 +23,11 @@ reqwest-native-tls = ["reqwest", "reqwest?/native-tls"] [dependencies] bytes = "1" -ironrdp-async = { path = "../ironrdp-async", version = "0.7" } # public -ironrdp-connector = { path = "../ironrdp-connector", version = "0.7", optional = true } +ironrdp-async = { path = "../ironrdp-async", version = "0.8" } # public +ironrdp-connector = { path = "../ironrdp-connector", version = "0.8", optional = true } tokio = { version = "1", features = ["io-util"] } reqwest = { version = "0.12", default-features = false, features = ["http2", "system-proxy"], optional = true } -sspi = { version = "0.16", features = ["network_client", "dns_resolver"], optional = true } +sspi = { version = "0.18", features = ["network_client", "dns_resolver"], optional = true } url = { version = "2.5", optional = true } [lints] diff --git a/crates/ironrdp-tokio/src/lib.rs b/crates/ironrdp-tokio/src/lib.rs index 010b2219..b188e84b 100644 --- a/crates/ironrdp-tokio/src/lib.rs +++ b/crates/ironrdp-tokio/src/lib.rs @@ -158,3 +158,66 @@ where }) } } + +pub type MovableTokioFramed = Framed>; + +pub struct MovableTokioStream { + inner: S, +} + +impl StreamWrapper for MovableTokioStream { + type InnerStream = S; + + fn from_inner(stream: Self::InnerStream) -> Self { + Self { inner: stream } + } + + fn into_inner(self) -> Self::InnerStream { + self.inner + } + + fn get_inner(&self) -> &Self::InnerStream { + &self.inner + } + + fn get_inner_mut(&mut self) -> &mut Self::InnerStream { + &mut self.inner + } +} + +impl FramedRead for MovableTokioStream +where + S: Send + Unpin + AsyncRead, +{ + type ReadFut<'read> + = Pin> + Send + 'read>> + where + Self: 'read; + + fn read<'a>(&'a mut self, buf: &'a mut BytesMut) -> Self::ReadFut<'a> { + use tokio::io::AsyncReadExt as _; + + Box::pin(async { self.inner.read_buf(buf).await }) + } +} + +impl FramedWrite for MovableTokioStream +where + S: Send + Unpin + AsyncWrite, +{ + type WriteAllFut<'write> + = Pin> + Send + 'write>> + where + Self: 'write; + + fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteAllFut<'a> { + use tokio::io::AsyncWriteExt as _; + + Box::pin(async { + self.inner.write_all(buf).await?; + self.inner.flush().await?; + + Ok(()) + }) + } +} diff --git a/crates/ironrdp-tokio/src/reqwest.rs b/crates/ironrdp-tokio/src/reqwest.rs index 68060c27..deeee192 100644 --- a/crates/ironrdp-tokio/src/reqwest.rs +++ b/crates/ironrdp-tokio/src/reqwest.rs @@ -1,26 +1,21 @@ -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 _}; use tokio::net::{TcpStream, UdpSocket}; use url::Url; -use crate::AsyncNetworkClient; +use crate::NetworkClient; pub struct ReqwestNetworkClient { client: Option, } -impl AsyncNetworkClient for ReqwestNetworkClient { - fn send<'a>( - &'a mut self, - network_request: &'a sspi::generator::NetworkRequest, - ) -> Pin>> + 'a>> { - Box::pin(ReqwestNetworkClient::send_request(self, network_request)) +impl NetworkClient for ReqwestNetworkClient { + async fn send(&mut self, network_request: &sspi::generator::NetworkRequest) -> ConnectorResult> { + ReqwestNetworkClient::send_request(self, network_request).await } } @@ -62,7 +57,10 @@ impl ReqwestNetworkClient { .map_err(|e| Error::new(ErrorKind::NoAuthenticatingAuthority, format!("{e:?}"))) .map_err(|e| custom_err!("failed to send KDC request over TCP", e))?; - let mut buf = vec![0; len as usize + 4]; + let len = usize::try_from(len) + .map_err(|_| general_err!("invalid buffer length: out of range integral type conversion"))?; + + let mut buf = vec![0; len + 4]; buf[0..4].copy_from_slice(&(len.to_be_bytes())); stream diff --git a/crates/ironrdp-web/src/canvas.rs b/crates/ironrdp-web/src/canvas.rs index fcb7464e..96b9df50 100644 --- a/crates/ironrdp-web/src/canvas.rs +++ b/crates/ironrdp-web/src/canvas.rs @@ -1,5 +1,6 @@ use core::num::NonZeroU32; +use anyhow::Context as _; use ironrdp::pdu::geometry::{InclusiveRectangle, Rectangle as _}; use softbuffer::{NoDisplayHandle, NoWindowHandle}; use web_sys::HtmlCanvasElement; @@ -61,7 +62,7 @@ impl Canvas { let region_width_usize = usize::from(region_width); for dst_row in dst - .chunks_exact_mut(self.width.get() as usize) + .chunks_exact_mut(usize::try_from(self.width.get()).context("canvas width")?) .skip(region_top_usize) .take(region_height_usize) { diff --git a/crates/ironrdp-web/src/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/network_client.rs b/crates/ironrdp-web/src/network_client.rs index f22a3b03..0e32efe8 100644 --- a/crates/ironrdp-web/src/network_client.rs +++ b/crates/ironrdp-web/src/network_client.rs @@ -1,53 +1,45 @@ -use core::pin::Pin; - -use futures_util::Future; use ironrdp::connector::sspi::generator::NetworkRequest; use ironrdp::connector::sspi::network_client::NetworkProtocol; use ironrdp::connector::{custom_err, reason_err, ConnectorResult}; -use ironrdp_futures::AsyncNetworkClient; +use ironrdp_futures::NetworkClient; use tracing::debug; #[derive(Debug)] pub(crate) struct WasmNetworkClient; -impl AsyncNetworkClient for WasmNetworkClient { - fn send<'a>( - &'a mut self, - network_request: &'a NetworkRequest, - ) -> Pin>> + 'a>> { - Box::pin(async move { - debug!(?network_request.protocol, ?network_request.url); +impl NetworkClient for WasmNetworkClient { + async fn send(&mut self, network_request: &NetworkRequest) -> ConnectorResult> { + debug!(?network_request.protocol, ?network_request.url); - match &network_request.protocol { - NetworkProtocol::Http | NetworkProtocol::Https => { - let body = js_sys::Uint8Array::from(network_request.data.as_slice()); + match &network_request.protocol { + NetworkProtocol::Http | NetworkProtocol::Https => { + let body = js_sys::Uint8Array::from(network_request.data.as_slice()); - let response = gloo_net::http::Request::post(network_request.url.as_str()) - .header("keep-alive", "true") - .body(body) - .map_err(|e| custom_err!("failed to send KDC request", e))? - .send() - .await - .map_err(|e| custom_err!("failed to send KDC request", e))?; + let response = gloo_net::http::Request::post(network_request.url.as_str()) + .header("keep-alive", "true") + .body(body) + .map_err(|e| custom_err!("failed to send KDC request", e))? + .send() + .await + .map_err(|e| custom_err!("failed to send KDC request", e))?; - if !response.ok() { - return Err(reason_err!( - "KdcProxy", - "HTTP status error ({} {})", - response.status(), - response.status_text(), - )); - } - - let body = response - .binary() - .await - .map_err(|e| custom_err!("failed to retrieve HTTP response", e))?; - - Ok(body) + if !response.ok() { + return Err(reason_err!( + "KdcProxy", + "HTTP status error ({} {})", + response.status(), + response.status_text(), + )); } - unsupported => Err(reason_err!("CredSSP", "unsupported protocol: {unsupported:?}")), + + let body = response + .binary() + .await + .map_err(|e| custom_err!("failed to retrieve HTTP response", e))?; + + Ok(body) } - }) + unsupported => Err(reason_err!("CredSSP", "unsupported protocol: {unsupported:?}")), + } } } diff --git a/crates/ironrdp-web/src/session.rs b/crates/ironrdp-web/src/session.rs index a3e189a7..6d0e018a 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")?; } @@ -979,11 +979,11 @@ async fn connect( let connection_result = ironrdp_futures::connect_finalize( upgraded, - &mut framed, connector, + &mut framed, + &mut WasmNetworkClient, (&destination).into(), server_public_key, - Some(&mut WasmNetworkClient), url::Url::parse(kdc_proxy_url.unwrap_or_default().as_str()) // if kdc_proxy_url does not exit, give url parser a empty string, it will fail anyway and map to a None .ok() .map(|url| KerberosConfig { @@ -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/CHANGELOG.md b/crates/ironrdp/CHANGELOG.md index 848a10d0..48a74967 100644 --- a/crates/ironrdp/CHANGELOG.md +++ b/crates/ironrdp/CHANGELOG.md @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [[0.14.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-v0.13.0...ironrdp-v0.14.0)] - 2025-12-18 + +### Build + +- Bump picky and sspi ([#1028](https://github.com/Devolutions/IronRDP/issues/1028)) ([5bd319126d](https://github.com/Devolutions/IronRDP/commit/5bd319126d32fbd8e505508e27ab2b1a18a83d04)) + + This fixes build issues with some dependencies. + ## [[0.13.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-v0.12.0...ironrdp-v0.13.0)] - 2025-09-24 ### Build diff --git a/crates/ironrdp/Cargo.toml b/crates/ironrdp/Cargo.toml index cd7b5850..25945735 100644 --- a/crates/ironrdp/Cargo.toml +++ b/crates/ironrdp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ironrdp" -version = "0.13.0" +version = "0.14.0" readme = "README.md" description = "A meta crate re-exporting IronRDP crates for convenience" edition.workspace = true @@ -40,28 +40,28 @@ __bench = ["ironrdp-server/__bench"] [dependencies] ironrdp-core = { path = "../ironrdp-core", version = "0.1", optional = true } # public ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6", optional = true } # public -ironrdp-cliprdr = { path = "../ironrdp-cliprdr", version = "0.4", optional = true } # public -ironrdp-connector = { path = "../ironrdp-connector", version = "0.7", optional = true } # public -ironrdp-acceptor = { path = "../ironrdp-acceptor", version = "0.7", optional = true } # public -ironrdp-session = { path = "../ironrdp-session", version = "0.7", optional = true } # public -ironrdp-graphics = { path = "../ironrdp-graphics", version = "0.6", optional = true } # public +ironrdp-cliprdr = { path = "../ironrdp-cliprdr", version = "0.5", optional = true } # public +ironrdp-connector = { path = "../ironrdp-connector", version = "0.8", optional = true } # public +ironrdp-acceptor = { path = "../ironrdp-acceptor", version = "0.8", optional = true } # public +ironrdp-session = { path = "../ironrdp-session", version = "0.8", optional = true } # public +ironrdp-graphics = { path = "../ironrdp-graphics", version = "0.7", optional = true } # public ironrdp-input = { path = "../ironrdp-input", version = "0.4", optional = true } # public -ironrdp-server = { path = "../ironrdp-server", version = "0.9", optional = true, features = ["helper"] } # public +ironrdp-server = { path = "../ironrdp-server", version = "0.10", optional = true, features = ["helper"] } # public ironrdp-svc = { path = "../ironrdp-svc", version = "0.5", optional = true } # public ironrdp-dvc = { path = "../ironrdp-dvc", version = "0.4", optional = true } # public -ironrdp-rdpdr = { path = "../ironrdp-rdpdr", version = "0.4", optional = true } # public +ironrdp-rdpdr = { path = "../ironrdp-rdpdr", version = "0.5", optional = true } # public ironrdp-rdpsnd = { path = "../ironrdp-rdpsnd", version = "0.6", optional = true } # public ironrdp-displaycontrol = { path = "../ironrdp-displaycontrol", version = "0.4", optional = true } # public [dev-dependencies] -ironrdp-blocking = { path = "../ironrdp-blocking", version = "0.7.0" } -ironrdp-cliprdr-native = { path = "../ironrdp-cliprdr-native", version = "0.4.0" } +ironrdp-blocking = { path = "../ironrdp-blocking", version = "0.8.0" } +ironrdp-cliprdr-native = { path = "../ironrdp-cliprdr-native", version = "0.5.0" } anyhow = "1" async-trait = "0.1" image = { version = "0.25.6", default-features = false, features = ["png"] } pico-args = "0.5" x509-cert = { version = "0.2", default-features = false, features = ["std"] } -sspi = { version = "0.16", features = ["network_client"] } +sspi = { version = "0.18", features = ["network_client"] } tracing = { version = "0.1", features = ["log"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] } tokio-rustls = "0.26" diff --git a/crates/ironrdp/examples/screenshot.rs b/crates/ironrdp/examples/screenshot.rs index 94144ce6..64ff33ac 100644 --- a/crates/ironrdp/examples/screenshot.rs +++ b/crates/ironrdp/examples/screenshot.rs @@ -258,11 +258,11 @@ fn connect( let mut network_client = ReqwestNetworkClient; let connection_result = ironrdp_blocking::connect_finalize( upgraded, - &mut upgraded_framed, connector, + &mut upgraded_framed, + &mut network_client, server_name.into(), server_public_key, - &mut network_client, None, ) .context("finalize connection")?; 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..574b77d3 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"] } -sspi = { version = "0.16", features = ["network_client"] } +ironrdp-rdcleanpath.path = "../crates/ironrdp-rdcleanpath" +sspi = { version = "0.18", 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/Devolutions.IronRdp.Build.iOS.props b/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.Build.iOS.props index af8d97cc..b692fdbb 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.Build.iOS.props +++ b/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.Build.iOS.props @@ -1,6 +1,6 @@ - net8.0-ios + net9.0-ios 12.1 diff --git a/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.csproj b/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.csproj index 5ae81afe..38fc2e65 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.csproj +++ b/ffi/dotnet/Devolutions.IronRdp/Devolutions.IronRdp.csproj @@ -4,7 +4,7 @@ Devolutions Bindings to Rust IronRDP native library latest - 2025.9.24.0 + 2025.12.4.0 enable enable true 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..11ae4e2b 100644 --- a/ffi/src/error.rs +++ b/ffi/src/error.rs @@ -6,101 +6,15 @@ 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 { - ironrdp::connector::ConnectorErrorKind::Encode(_) => IronRdpErrorKind::EncodeError, - ironrdp::connector::ConnectorErrorKind::Decode(_) => IronRdpErrorKind::DecodeError, - ironrdp::connector::ConnectorErrorKind::Credssp(_) => IronRdpErrorKind::CredsspError, - ironrdp::connector::ConnectorErrorKind::AccessDenied => IronRdpErrorKind::AccessDenied, - _ => IronRdpErrorKind::Generic, - } - } -} +pub struct GenericError(pub anyhow::Error); -impl From<&str> for IronRdpErrorKind { - fn from(_val: &str) -> Self { - IronRdpErrorKind::Generic - } -} - -impl From for IronRdpErrorKind { - fn from(_val: sspi::Error) -> Self { - IronRdpErrorKind::CredsspError - } -} - -impl From for IronRdpErrorKind { - fn from(_val: ironrdp::pdu::PduError) -> Self { - IronRdpErrorKind::PduError - } -} - -impl From for IronRdpErrorKind { - fn from(_val: ironrdp::core::EncodeError) -> Self { - IronRdpErrorKind::EncodeError - } -} - -impl From for IronRdpErrorKind { - fn from(_val: ironrdp::core::DecodeError) -> Self { - IronRdpErrorKind::DecodeError - } -} - -impl From for IronRdpErrorKind { - fn from(_: std::io::Error) -> Self { - IronRdpErrorKind::IO - } -} - -impl From for IronRdpErrorKind { - fn from(_val: core::fmt::Error) -> Self { - IronRdpErrorKind::Generic - } -} - -impl From for IronRdpErrorKind { - fn from(value: SessionError) -> Self { - match value.kind() { - ironrdp::session::SessionErrorKind::Pdu(_) => IronRdpErrorKind::PduError, - ironrdp::session::SessionErrorKind::Encode(_) => IronRdpErrorKind::EncodeError, - ironrdp::session::SessionErrorKind::Decode(_) => IronRdpErrorKind::DecodeError, - _ => IronRdpErrorKind::Generic, - } - } -} - -impl From<&dyn ClipboardError> for IronRdpErrorKind { - fn from(_val: &dyn ClipboardError) -> Self { - IronRdpErrorKind::Clipboard - } -} - -#[cfg(target_os = "windows")] -impl From for IronRdpErrorKind { - fn from(_val: WinCliprdrError) -> Self { - IronRdpErrorKind::Clipboard - } -} - -impl From for IronRdpErrorKind { - fn from(_val: WrongOSError) -> Self { - IronRdpErrorKind::WrongOS - } -} - -impl From for Box -where - T: Into + ToString, -{ - fn from(value: T) -> Self { - let repr = value.to_string(); - let kind = value.into(); - Box::new(ffi::IronRdpError(IronRdpErrorInner { repr, kind })) +impl Display for GenericError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:#}", self.0) } } @@ -109,6 +23,163 @@ struct IronRdpErrorInner { kind: IronRdpErrorKind, } +// Helper function to create an IronRdpError +fn make_ffi_error(repr: String, kind: IronRdpErrorKind) -> Box { + Box::new(ffi::IronRdpError(IronRdpErrorInner { repr, kind })) +} + +// Direct conversion from IronRdpErrorKind (for cases with no underlying error) +impl From for Box { + fn from(kind: IronRdpErrorKind) -> Self { + make_ffi_error(kind.to_string(), kind) + } +} + +// IronRDP errors - use .report() to include full error chain with sources +impl From for Box { + fn from(value: ConnectorError) -> Self { + let kind = match value.kind() { + ironrdp::connector::ConnectorErrorKind::Encode(_) => IronRdpErrorKind::EncodeError, + ironrdp::connector::ConnectorErrorKind::Decode(_) => IronRdpErrorKind::DecodeError, + ironrdp::connector::ConnectorErrorKind::Credssp(_) => IronRdpErrorKind::CredsspError, + ironrdp::connector::ConnectorErrorKind::AccessDenied => IronRdpErrorKind::AccessDenied, + _ => IronRdpErrorKind::Generic, + }; + let repr = value.report().to_string(); + make_ffi_error(repr, kind) + } +} + +impl From for Box { + fn from(value: SessionError) -> Self { + let kind = match value.kind() { + ironrdp::session::SessionErrorKind::Pdu(_) => IronRdpErrorKind::PduError, + ironrdp::session::SessionErrorKind::Encode(_) => IronRdpErrorKind::EncodeError, + ironrdp::session::SessionErrorKind::Decode(_) => IronRdpErrorKind::DecodeError, + _ => IronRdpErrorKind::Generic, + }; + let repr = value.report().to_string(); + make_ffi_error(repr, kind) + } +} + +impl From for Box { + fn from(value: ironrdp::pdu::PduError) -> Self { + let repr = value.report().to_string(); + make_ffi_error(repr, IronRdpErrorKind::PduError) + } +} + +impl From for Box { + fn from(value: ironrdp::core::EncodeError) -> Self { + let repr = value.report().to_string(); + make_ffi_error(repr, IronRdpErrorKind::EncodeError) + } +} + +impl From for Box { + fn from(value: ironrdp::core::DecodeError) -> Self { + let repr = value.report().to_string(); + make_ffi_error(repr, IronRdpErrorKind::DecodeError) + } +} + +// std::io::Error - convert to anyhow::Error for proper source chain formatting +impl From for Box { + fn from(value: std::io::Error) -> Self { + let repr = format!("{:#}", anyhow::Error::new(value)); + make_ffi_error(repr, IronRdpErrorKind::IO) + } +} + +// sspi::Error - convert to anyhow::Error for proper source chain formatting +impl From for Box { + fn from(value: sspi::Error) -> Self { + let repr = format!("{:#}", anyhow::Error::new(value)); + make_ffi_error(repr, IronRdpErrorKind::CredsspError) + } +} + +// Simple string error +impl From<&str> for Box { + fn from(value: &str) -> Self { + make_ffi_error(value.to_owned(), IronRdpErrorKind::Generic) + } +} + +// core::fmt::Error - convert to anyhow::Error for consistency +impl From for Box { + fn from(value: core::fmt::Error) -> Self { + let repr = format!("{:#}", anyhow::Error::new(value)); + make_ffi_error(repr, IronRdpErrorKind::Generic) + } +} + +// Clipboard errors - manually format with full source chain +impl From<&dyn ClipboardError> for Box { + fn from(value: &dyn ClipboardError) -> Self { + use core::fmt::Write as _; + + // Manually build error chain since we have a trait object reference + let mut repr = value.to_string(); + let mut source = value.source(); + while let Some(e) = source { + let _ = write!(&mut repr, ", caused by: {e}"); + source = e.source(); + } + make_ffi_error(repr, IronRdpErrorKind::Clipboard) + } +} + +#[cfg(target_os = "windows")] +impl From for Box { + fn from(value: WinCliprdrError) -> Self { + let repr = format!("{:#}", anyhow::Error::new(value)); + make_ffi_error(repr, IronRdpErrorKind::Clipboard) + } +} + +// DER errors - convert to anyhow::Error for proper source chain formatting +impl From for Box { + fn from(value: der::Error) -> Self { + let repr = format!("{:#}", anyhow::Error::new(value)); + make_ffi_error(repr, IronRdpErrorKind::DecodeError) + } +} + +impl From for Box { + fn from(value: ironrdp_rdcleanpath::MissingRDCleanPathField) -> Self { + let repr = format!("{:#}", anyhow::Error::new(value)); + make_ffi_error(repr, IronRdpErrorKind::Generic) + } +} + +// GenericError already has proper Display impl with {:#} +impl From for Box { + fn from(value: GenericError) -> Self { + make_ffi_error(value.to_string(), IronRdpErrorKind::Generic) + } +} + +// FFI-specific errors +impl From for Box { + fn from(value: ValueConsumedError) -> Self { + make_ffi_error(value.to_string(), IronRdpErrorKind::Consumed) + } +} + +impl From for Box { + fn from(value: IncorrectEnumTypeError) -> Self { + make_ffi_error(value.to_string(), IronRdpErrorKind::IncorrectEnumType) + } +} + +impl From for Box { + fn from(value: WrongOSError) -> Self { + make_ffi_error(value.to_string(), IronRdpErrorKind::WrongOS) + } +} + #[diplomat::bridge] pub mod ffi { use core::fmt::Write as _; @@ -221,11 +292,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..5d8e890a 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -63,9 +63,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" [[package]] name = "bit_field" @@ -75,9 +75,9 @@ checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" [[package]] name = "bitflags" -version = "2.9.4" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "bitvec" @@ -108,9 +108,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.2.39" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ "find-msvc-tools", "jobserver", @@ -120,9 +120,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "const-oid" @@ -150,9 +150,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -237,9 +237,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.2" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "flagset" @@ -249,9 +249,9 @@ checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" [[package]] name = "flate2" -version = "1.1.2" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "miniz_oxide", @@ -275,19 +275,19 @@ 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]] name = "ironrdp-cliprdr" -version = "0.4.0" +version = "0.5.0" dependencies = [ "bitflags", "ironrdp-core", @@ -363,7 +363,7 @@ dependencies = [ [[package]] name = "ironrdp-graphics" -version = "0.6.0" +version = "0.7.0" dependencies = [ "bit_field", "bitflags", @@ -371,7 +371,6 @@ dependencies = [ "byteorder", "ironrdp-core", "ironrdp-pdu", - "lazy_static", "num-derive", "num-traits", "yuv", @@ -401,7 +400,7 @@ dependencies = [ [[package]] name = "ironrdp-rdpdr" -version = "0.4.1" +version = "0.5.0" dependencies = [ "bitflags", "ironrdp-core", @@ -441,17 +440,11 @@ 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" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libfuzzer-sys" @@ -465,9 +458,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "md-5" @@ -587,18 +580,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.41" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -643,9 +636,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "slab" @@ -665,9 +658,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.106" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -734,9 +727,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" dependencies = [ "log", "pin-project-lite", @@ -746,9 +739,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", @@ -757,24 +750,24 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" dependencies = [ "once_cell", ] [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unicode-ident" -version = "1.0.19" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "version_check" @@ -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" @@ -829,9 +813,9 @@ dependencies = [ [[package]] name = "yuv" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30078f3e5790a2127f89c57c4ccb46d060205bf2c4c267f77cd08fb5c02c6d79" +checksum = "28f1bad143caadcfcaec93039dc9c40a30fc86f23d9e7cc03764a39fe51d9d43" dependencies = [ "num-traits", ] diff --git a/release-plz.toml b/release-plz.toml index 79409f23..8a33bf6f 100644 --- a/release-plz.toml +++ b/release-plz.toml @@ -11,6 +11,7 @@ release_commits = "^(feat|docs|fix|build|perf)" [[package]] name = "ironrdp-client" git_release_enable = true +publish = false # TODO: enable publishing when ready. # ironrdp-tls does not compile if no backend is specified. # rustls is the most common backend, so we let cargo publish check with it. 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) }