Merge branch 'master' into ironrdp-web-add-clippy-index-slicing-lint

This commit is contained in:
Alex Yusiuk 2025-11-06 10:47:07 +02:00 committed by GitHub
commit 421d1330d3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
158 changed files with 4737 additions and 2142 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 dont need to do that for ninja.
# We don't need to do that for ninja.
Write-Output "PATH=$Env:PATH;$Env:ProgramFiles\NASM" >> $Env:GITHUB_ENV
# libclang / LLVM is a requirement for AWS LC.
@ -125,6 +124,7 @@ jobs:
# Install Visual Studio Developer PowerShell Module for cmdlets such as Enter-VsDevShell
Install-Module VsDevShell -Force
shell: pwsh
# No pre-generated bindings for Android and iOS.
# https://aws.github.io/aws-lc-rs/platform_support.html#pre-generated-bindings
@ -141,19 +141,18 @@ jobs:
sudo apt-get install gcc-multilib
- name: Setup LLVM
if: ${{ matrix.os == 'linux' }}
uses: Devolutions/actions-public/setup-llvm@v1
if: matrix.os == 'linux'
with:
version: "18.1.8"
- name: Setup CBake
if: ${{ matrix.os == 'linux' }}
uses: Devolutions/actions-public/setup-cbake@v1
if: matrix.os == 'linux'
with:
cargo_env_scripts: true
- name: Build native lib (${{matrix.os}}-${{matrix.arch}})
shell: pwsh
run: |
$DotNetOs = '${{matrix.os}}'
$DotNetArch = '${{matrix.arch}}'
@ -210,6 +209,7 @@ jobs:
$OutputPath = Join-Path "dependencies" "runtimes" $DotNetRid "native"
New-Item -ItemType Directory -Path $OutputPath | Out-Null
Copy-Item $OutputLibrary $(Join-Path $OutputPath $RenamedLibraryName)
shell: pwsh
- name: Upload native components
uses: actions/upload-artifact@v4
@ -219,8 +219,8 @@ jobs:
build-universal:
name: Universal build
needs: [preflight, build-native]
runs-on: ubuntu-22.04
needs: [ preflight, build-native ]
strategy:
fail-fast: false
matrix:
@ -239,7 +239,6 @@ jobs:
path: dependencies/runtimes
- name: Lipo native components
shell: pwsh
run: |
Set-Location "dependencies/runtimes"
# No RID for universal binaries, see: https://github.com/dotnet/runtime/issues/53156
@ -249,9 +248,9 @@ jobs:
$LipoCmd = $(@('lipo', '-create', '-output', (Join-Path -Path $OutputPath -ChildPath "libDevolutionsIronRdp.dylib")) + $Libraries) -Join ' '
Write-Host $LipoCmd
Invoke-Expression $LipoCmd
shell: pwsh
- name: Framework
shell: pwsh
if: ${{ matrix.os == 'ios' }}
run: |
$Version = '${{ needs.preflight.outputs.project-version }}'
@ -269,19 +268,19 @@ jobs:
[xml] $InfoPlistXml = Get-Content (Join-Path "ffi" "dotnet" "Devolutions.IronRdp" "Info.plist")
Select-Xml -xml $InfoPlistXml -XPath "/plist/dict/key[. = 'CFBundleIdentifier']/following-sibling::string[1]" |
%{
%{
$_.Node.InnerXml = "com.devolutions.ironrdp"
}
Select-Xml -xml $InfoPlistXml -XPath "/plist/dict/key[. = 'CFBundleExecutable']/following-sibling::string[1]" |
%{
%{
$_.Node.InnerXml = $BundleName
}
Select-Xml -xml $InfoPlistXml -XPath "/plist/dict/key[. = 'CFBundleVersion']/following-sibling::string[1]" |
%{
%{
$_.Node.InnerXml = $Version
}
Select-Xml -xml $InfoPlistXml -XPath "/plist/dict/key[. = 'CFBundleShortVersionString']/following-sibling::string[1]" |
%{
%{
$_.Node.InnerXml = $ShortVersion
}
@ -294,6 +293,7 @@ jobs:
# .NET XML document inserts two square brackets at the end of the DOCTYPE tag
# It's perfectly valid XML, but we're dealing with plists here and dyld will not be able to read the file
((Get-Content -Path (Join-Path $FrameworkDir "Info.plist") -Raw) -Replace 'PropertyList-1.0.dtd"\[\]', 'PropertyList-1.0.dtd"') | Set-Content -Path (Join-Path $FrameworkDir "Info.plist")
shell: pwsh
- name: Upload native components
uses: actions/upload-artifact@v4
@ -303,8 +303,8 @@ jobs:
build-managed:
name: Managed build
needs: [build-universal]
runs-on: windows-2022
needs: build-universal
steps:
- name: Check out ${{ github.repository }}
@ -317,9 +317,9 @@ jobs:
run: dotnet workload install ios
- name: Prepare dependencies
shell: pwsh
run: |
New-Item -ItemType Directory -Path "dependencies/runtimes" | Out-Null
shell: pwsh
- name: Download native components
uses: actions/download-artifact@v4
@ -327,19 +327,19 @@ jobs:
path: dependencies/runtimes
- name: Rename dependencies
shell: pwsh
run: |
Set-Location "dependencies/runtimes"
$(Get-Item ".\ironrdp-*") | ForEach-Object { Rename-Item $_ $_.Name.Replace("ironrdp-", "") }
Get-ChildItem * -Recurse
shell: pwsh
- name: Build Devolutions.IronRdp (managed)
shell: pwsh
run: |
# net8.0 target packaged as Devolutions.IronRdp
dotnet build .\ffi\dotnet\Devolutions.IronRdp\Devolutions.IronRdp.csproj -c Release
# net8.0-ios target packaged as Devolutions.IronRdp.iOS
dotnet build .\ffi\dotnet\Devolutions.IronRdp\Devolutions.IronRdp.csproj -c Release /p:PackageId=Devolutions.IronRdp.iOS
shell: pwsh
- name: Upload managed components
uses: actions/upload-artifact@v4
@ -349,12 +349,10 @@ jobs:
publish:
name: Publish NuGet package
runs-on: ubuntu-latest
environment: nuget-publish
if: needs.preflight.outputs.dry-run == 'false'
needs:
- preflight
- build-managed
if: ${{ needs.preflight.outputs.dry-run == 'false' }}
needs: [preflight, build-managed]
runs-on: ubuntu-latest
steps:
- name: Download NuGet package artifact
@ -364,15 +362,14 @@ jobs:
path: package
- name: Publish to nuget.org
shell: pwsh
run: |
$Files = Get-ChildItem -Recurse package/*.nupkg
foreach ($File in $Files) {
$PushCmd = @(
'dotnet',
'nuget',
'push',
'dotnet',
'nuget',
'push',
"$File",
'--api-key',
'${{ secrets.NUGET_API_KEY }}',
@ -385,19 +382,17 @@ jobs:
$PushCmd = $PushCmd -Join ' '
Invoke-Expression $PushCmd
}
shell: pwsh
notify:
name: Notify failure
runs-on: ubuntu-latest
if: ${{ always() && contains(needs.*.result, 'failure') && github.event_name == 'schedule' }}
needs:
- preflight
- build-native
- build-universal
- build-managed
needs: [preflight, build-native, build-universal, build-managed]
runs-on: ubuntu-latest
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_ARCHITECTURE }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
steps:
- name: Send slack notification
id: slack

View file

@ -14,9 +14,8 @@ jobs:
# Create a PR with the new versions and changelog, preparing the next release.
open-pr:
name: Open release PR
runs-on: ubuntu-latest
environment: cratesio-publish
runs-on: ubuntu-latest
concurrency:
group: release-plz-${{ github.ref }}
cancel-in-progress: false
@ -37,7 +36,6 @@ jobs:
github-token: ${{ secrets.DEVOLUTIONSBOT_WRITE_TOKEN }}
- name: Update fuzz/Cargo.lock
shell: pwsh
if: ${{ steps.release-plz.outputs.did-open-pr == 'true' }}
run: |
$prRaw = '${{ steps.release-plz.outputs.pr }}'
@ -61,12 +59,13 @@ jobs:
Write-Host "Update the release pull request"
git push --force
shell: pwsh
# Release unpublished packages.
release:
name: Release crates
runs-on: ubuntu-latest
environment: cratesio-publish
runs-on: ubuntu-latest
steps:
- name: Checkout repository

25
Cargo.lock generated
View file

@ -708,9 +708,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.48"
version = "4.5.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae"
checksum = "0c2cfd7bf8a6017ddaa4e32ffe7403d547790db06bd171c1c53926faab501623"
dependencies = [
"clap_builder",
"clap_derive",
@ -718,9 +718,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.48"
version = "4.5.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9"
checksum = "0a4c05b9e80c5ccd3a7ef080ad7b6ba7d6fc00a985b8b157197075677c82c7a0"
dependencies = [
"anstream",
"anstyle",
@ -730,9 +730,9 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.5.47"
version = "4.5.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c"
checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
dependencies = [
"heck",
"proc-macro2",
@ -1527,6 +1527,7 @@ dependencies = [
name = "ffi"
version = "0.0.0"
dependencies = [
"anyhow",
"diplomat",
"diplomat-runtime",
"embed-resource",
@ -1534,6 +1535,7 @@ dependencies = [
"ironrdp-cliprdr-native",
"ironrdp-core",
"ironrdp-dvc-pipe-proxy",
"ironrdp-rdcleanpath",
"sspi",
"thiserror 2.0.17",
"tracing",
@ -2597,7 +2599,6 @@ dependencies = [
"expect-test",
"ironrdp-core",
"ironrdp-pdu",
"lazy_static",
"num-derive",
"num-traits",
"yuv",
@ -2643,7 +2644,6 @@ dependencies = [
"expect-test",
"ironrdp-core",
"ironrdp-error",
"lazy_static",
"md-5",
"num-bigint",
"num-derive",
@ -2812,7 +2812,6 @@ dependencies = [
"ironrdp-rdpfile",
"ironrdp-rdpsnd",
"ironrdp-session",
"lazy_static",
"paste",
"png",
"pretty_assertions",
@ -3712,9 +3711,9 @@ dependencies = [
[[package]]
name = "opus2"
version = "0.3.2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8e79f6e5198dfc9ec913fd4ddc8b53b87263d59c500b989f8449bd566552ce3"
checksum = "c3a4200c196ebf402d8a7091ce21845e8f2cd2f9c0c00deb73aaa14d024f6e6e"
dependencies = [
"libopus_sys",
]
@ -4440,9 +4439,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
[[package]]
name = "reqwest"
version = "0.12.23"
version = "0.12.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb"
checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f"
dependencies = [
"base64",
"bytes",

View file

@ -39,7 +39,6 @@ rstest = "0.25"
# Note: we are trying to move away from using these crates.
# They are being kept around for now for legacy compatibility,
# but new usage should be avoided.
lazy_static = "1.4" # Legacy crate; prefer std::sync::LazyLock or LazyCell
num-derive = "0.4"
num-traits = "0.2"
@ -94,7 +93,7 @@ ptr_cast_constness = "warn"
# == Correctness == #
#indexing_slicing = "warn" TODO: enable this lint project wide.
#as_conversions = "warn"
as_conversions = "warn"
cast_lossless = "warn"
cast_possible_truncation = "warn"
cast_possible_wrap = "warn"
@ -129,6 +128,7 @@ empty_enum_variants_with_brackets = "warn"
deref_by_slicing = "warn"
multiple_inherent_impl = "warn"
map_with_unused_argument_over_ranges = "warn"
partial_pub_fields = "warn"
trait_duplication_in_bounds = "warn"
type_repetition_in_bounds = "warn"
checked_conversions = "warn"

View file

@ -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:?}");

View file

@ -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

View file

@ -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 {

View file

@ -12,7 +12,7 @@ pub struct FinalizationSequence {
user_channel_id: u16,
io_channel_id: u16,
pub input_events: Vec<Vec<u8>>,
input_events: Vec<Vec<u8>>,
}
#[derive(Default, Debug)]
@ -190,6 +190,10 @@ impl FinalizationSequence {
}
}
pub fn into_input_events(self) -> Vec<Vec<u8>> {
self.input_events
}
pub fn is_done(&self) -> bool {
self.state.is_terminal()
}

View file

@ -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<RdpOutputEvent> 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<RdpOutputEvent> 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<RdpOutputEvent> 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<RdpOutputEvent> 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<RdpOutputEvent> 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,
},
));

View file

@ -235,7 +235,7 @@ async fn connect(
let upgraded = ironrdp_tokio::mark_as_upgraded(should_upgrade, &mut connector);
let erased_stream = Box::new(upgraded_stream) as Box<dyn AsyncReadWrite + Unpin + Send + Sync>;
let erased_stream: Box<dyn AsyncReadWrite + Unpin + Send + Sync> = Box::new(upgraded_stream);
let mut upgraded_framed = ironrdp_tokio::TokioFramed::new_with_leftover(erased_stream, leftover_bytes);
let connection_result = ironrdp_tokio::connect_finalize(
@ -336,7 +336,7 @@ async fn connect_ws(
.await?;
let (ws, leftover_bytes) = framed.into_inner();
let erased_stream = Box::new(ws) as Box<dyn AsyncReadWrite + Unpin + Send + Sync>;
let erased_stream: Box<dyn AsyncReadWrite + Unpin + Send + Sync> = Box::new(ws);
let upgraded_framed = ironrdp_tokio::TokioFramed::new_with_leftover(erased_stream, leftover_bytes);
Ok((connection_result, upgraded_framed))
@ -660,7 +660,7 @@ async fn active_session(
desktop_size,
enable_server_pointer,
pointer_software_rendering,
} = connection_activation.state
} = connection_activation.connection_activation_state()
{
debug!(?desktop_size, "Deactivation-Reactivation Sequence completed");
// Update image size with the new desktop size.

View file

@ -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::<u8>().cast_const();
if data.is_null() {
// Can't lock data handle, handle is not valid anymore (e.g. clipboard has changed)

View file

@ -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::<WinClipboardImpl>(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::<WinClipboardImpl>(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 => {

View file

@ -200,8 +200,14 @@ impl WinClipboard {
//
// SAFETY: `window` is a valid window handle, `clipboard_subproc` is in the static memory,
// `ctx` is valid and its ownership is transferred to the subclass via `into_raw`.
let winapi_result =
unsafe { SetWindowSubclass(window, Some(clipboard_subproc), 0, Box::into_raw(ctx) as usize) };
let winapi_result = unsafe {
SetWindowSubclass(
window,
Some(clipboard_subproc),
0,
Box::into_raw(ctx).expose_provenance(),
)
};
if winapi_result == FALSE {
return Err(WinCliprdrError::WindowSubclass);

View file

@ -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::<u8>(), data.len()) };
// SAFETY: We called `GlobalLock` on this handle just above.
if let Err(error) = unsafe { GlobalUnlock(handle) } {

View file

@ -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;

View file

@ -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<'_> {

View file

@ -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 {

View file

@ -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,

View file

@ -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,

View file

@ -406,7 +406,7 @@ pub trait ConnectorResultExt {
impl<T> ConnectorResultExt for ConnectorResult<T> {
fn with_context(self, context: &'static str) -> Self {
self.map_err(|mut e| {
e.context = context;
e.set_context(context);
e
})
}

View file

@ -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));

View file

@ -28,7 +28,7 @@ impl CompleteData {
}
fn process_data_first_pdu(&mut self, data_first: DataFirstPdu) -> DecodeResult<Option<Vec<u8>>> {
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<Option<Vec<u8>>> {
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 => {

View file

@ -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::<u8>::into(self.sp) << 2) | Into::<u8>::into(self.cb_id));
dst.write_u8(((self.cmd.as_u8()) << 4) | (Into::<u8>::into(self.sp) << 2) | Into::<u8>::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<u8> for Cmd {
type Error = DecodeError;
@ -282,12 +292,12 @@ impl From<Cmd> 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<u8>,
data: Vec<u8>,
}
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<u8> {
self.data
}
fn decode(header: Header, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
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<FieldType> for u8 {
#[derive(Debug, PartialEq)]
pub struct DataPdu {
header: Header,
pub channel_id: DynamicChannelId,
pub data: Vec<u8>,
channel_id: DynamicChannelId,
data: Vec<u8>,
}
impl DataPdu {
@ -447,6 +469,18 @@ impl DataPdu {
}
}
pub fn data(&self) -> &[u8] {
&self.data
}
pub fn into_data(self) -> Vec<u8> {
self.data
}
pub fn data_mut(&mut self) -> &mut Vec<u8> {
&mut self.data
}
fn decode(header: Header, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
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<CreationStatus> 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<Self> {
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<u16> for CapsVersion {
}
impl From<CapsVersion> 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<Self> {
ensure_size!(in: src, size: Self::headerless_fixed_part_size(&header));
let channel_id = header.cb_id.decode_val(src)?;

View file

@ -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"));
}

View file

@ -23,8 +23,8 @@ impl<T> Source for T where T: fmt::Display + fmt::Debug + Send + Sync + 'static
#[derive(Debug)]
pub struct Error<Kind> {
pub context: &'static str,
pub kind: Kind,
context: &'static str,
kind: Kind,
#[cfg(feature = "std")]
source: Option<Box<dyn core::error::Error + Sync + Send>>,
#[cfg(all(not(feature = "std"), feature = "alloc"))]
@ -80,6 +80,10 @@ impl<Kind> Error<Kind> {
&self.kind
}
pub fn set_context(&mut self, context: &'static str) {
self.context = context;
}
pub fn report(&self) -> ErrorReport<'_, Kind> {
ErrorReport(self)
}

View file

@ -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),
);
}

View file

@ -22,7 +22,6 @@ bitvec = "1.0"
ironrdp-core = { path = "../ironrdp-core", version = "0.1" } # public
ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6", features = ["std"] } # public
byteorder = "1.5" # TODO: remove
lazy_static.workspace = true # Legacy crate; prefer std::sync::LazyLock or LazyCell
num-derive.workspace = true # TODO: remove
num-traits.workspace = true # TODO: remove
yuv = { version = "0.8", features = ["rdp"] }

View file

@ -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)]

View file

@ -22,17 +22,21 @@ fn dwt_vertical<const SUBBAND_WIDTH: usize>(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<const SUBBAND_WIDTH: usize>(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<const SUBBAND_WIDTH: usize>(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
}

View file

@ -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;

View file

@ -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);

View file

@ -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)

View file

@ -195,8 +195,9 @@ impl<'a> BitmapStreamDecoderImpl<'a> {
}
fn write_aycocg_planes_to_rgb24(&self, params: AYCoCgParams, planes: &[u8], dst: &mut Vec<u8>) {
#![allow(clippy::similar_names)] // Its hard to find better names for co, cg, etc.
let sample_shift = params.chroma_subsampling as usize;
#![allow(clippy::similar_names, reason = "its 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);

View file

@ -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<I: Iterator> RleEncoderScanlineIterator<I> {
}
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 {

View file

@ -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<Region> = 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() {

View file

@ -63,17 +63,23 @@ impl<'a> BitStream<'a> {
}
pub fn encode(mode: EntropyAlgorithm, input: &[i16], tile: &mut [u8]) -> Result<usize, RlgrError> {
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<i16, RlgrError> {
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<i16, RlgrError> {
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<i16, RlgrError> {
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,
}
}
}

View file

@ -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::<LittleEndian>()? as usize;
let uncompressed_size = buffer.read_u32::<LittleEndian>()? as usize;
let segment_count = usize::from(buffer.read_u16::<LittleEndian>()?);
let uncompressed_size = usize::try_from(buffer.read_u32::<LittleEndian>()?)
.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::<LittleEndian>()? as usize;
let size = usize::try_from(buffer.read_u32::<LittleEndian>()?)
.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<SegmentedDataPdu<'static>> = 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<SegmentedDataPdu<'static>> =
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() {

View file

@ -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<u8>,
) -> io::Result<usize> {
) -> Result<usize, ZgfxError> {
// 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::<u32>()) as usize;
let distance = usize::try_from(distance_base + bits.split_to(distance_value_size).load_be::<u32>())
.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::<u32>() as usize;
let length = bits.split_to(15).load_be::<usize>();
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<u8>,
) -> io::Result<usize> {
) -> Result<usize, ZgfxError> {
// 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::<u32>() as usize;
let length = bits.split_to(length_token_size + 1).load_be::<usize>();
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,
}
}
}

View file

@ -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 }

View file

@ -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());

View file

@ -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<u16> for PktTy {
type Error = ();
@ -66,7 +76,7 @@ impl TryFrom<u16> 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<Self> {
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<Self> {
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),
})
}
}

View file

@ -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

View file

@ -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<BitmapUpdateData<'static>> = 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() {

View file

@ -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,

View file

@ -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<FastPathUpdatePdu<'static>> = 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() {

View file

@ -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<SurfaceCommand<'static>> = 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() {

View file

@ -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
}
}

View file

@ -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<Bitmap> = 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());
}

View file

@ -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<BitmapCache> = 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<BitmapCacheRev2> = 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<CellInfo> = LazyLock::new(|| CellInfo {
num_entries: 2555,
is_cache_persistent: true,
});
static CACHE_ENTRY: LazyLock<CacheEntry> = 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());
}

View file

@ -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
}

View file

@ -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<Guid> = LazyLock::new(|| {
Guid(
0xca8d_1bb9,
0x000f,
0x154f,
0x58,
0x9f,
0xae,
0x2d,
0x1a,
0x87,
0xe2,
0xd6,
)
});
static RFX_ICAP: LazyLock<RfxICap> = LazyLock::new(|| RfxICap {
flags: RfxICapFlags::CODEC_MODE,
entropy_bits: EntropyBits::Rlgr3,
});
static RFX_CAPSET: LazyLock<RfxCapset> = 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<RfxCaps> = 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<RfxClientCapsContainer> = 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<NsCodec> = LazyLock::new(|| NsCodec {
is_dynamic_fidelity_allowed: true,
is_subsampling_allowed: true,
color_loss_level: 3,
});
static CODEC: LazyLock<Codec> = 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<Codec> = LazyLock::new(|| Codec {
id: 0,
property: CodecProperty::ImageRemoteFx(RemoteFxContainer::ServerContainer(4)),
});
static BITMAP_CODECS: LazyLock<BitmapCodecs> = 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() {

View file

@ -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<Brush> = LazyLock::new(|| Brush {
support_level: SupportLevel::Color8x8,
});
#[test]
fn from_buffer_successfully_parses_brush_capset() {

View file

@ -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<General> = 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() {

View file

@ -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<GlyphCache> = 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<CacheDefinition> = LazyLock::new(|| CacheDefinition {
entries: 254,
max_cell_size: 4,
});
#[test]
fn from_buffer_correctly_parses_glyph_cache_capset() {

View file

@ -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<Input> = 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() {

View file

@ -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<OffscreenBitmapCache> = LazyLock::new(|| OffscreenBitmapCache {
is_supported: true,
cache_size: 7680,
cache_entries: 100,
});
#[test]
fn from_buffer_correctly_parses_offscreen_bitmap_cache_capset() {

View file

@ -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 {

View file

@ -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<Order> = 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() {

View file

@ -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<Pointer> = LazyLock::new(|| Pointer {
color_pointer_cache_size: 20,
pointer_cache_size: 21,
});
#[test]
fn from_buffer_correctly_parses_pointer_capset() {

View file

@ -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<Sound> = LazyLock::new(|| Sound {
flags: SoundFlags::BEEPS,
});
#[test]
fn from_buffer_correctly_parses_sound_capset() {

View file

@ -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<SurfaceCommands> = LazyLock::new(|| SurfaceCommands {
flags: CmdFlags::SET_SURFACE_BITS | CmdFlags::FRAME_MARKER | CmdFlags::STREAM_SURFACE_BITS,
});
#[test]
fn from_buffer_correctly_parses_surface_commands_capset() {

View file

@ -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<u32>,

View file

@ -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<VirtualChannel> = LazyLock::new(|| VirtualChannel {
flags: VirtualChannelFlags::COMPRESSION_SERVER_TO_CLIENT,
chunk_size: None,
});
static VIRTUAL_CHANNEL: LazyLock<VirtualChannel> = 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();

View file

@ -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
}

View file

@ -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<LicensePdu> = 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<u8> = {
let username_len = CLIENT_USERNAME.len() + UTF8_NULL_TERMINATOR_SIZE;
let mut username_len_buf = Vec::new();
username_len_buf.write_u16::<LittleEndian>(u16::try_from(username_len).expect("can't panic")).unwrap();
static REQUEST_BUFFER: LazyLock<Vec<u8>> = LazyLock::new(|| {
let username_len = CLIENT_USERNAME.len() + UTF8_NULL_TERMINATOR_SIZE;
let mut username_len_buf = Vec::new();
username_len_buf
.write_u16::<LittleEndian>(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::<LittleEndian>(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::<LittleEndian>(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<LicensePdu> = 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() {

View file

@ -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<PlatformChallengeResponseData> = 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<ClientHardwareIdentification> =
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<LicensePdu> = 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() {

View file

@ -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<LicensePdu> = 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() {

View file

@ -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<ProprietaryCertificate> = 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<ProductInfo> = LazyLock::new(|| ProductInfo {
version: 0x60000,
company_name: "Microsoft Corporation".to_owned(),
product_id: "A02".to_owned(),
});
static PUBLIC_KEY: LazyLock<RsaPublicKey> = LazyLock::new(|| RsaPublicKey {
public_exponent: 0x0001_0001,
modulus: Vec::from(MODULUS.as_ref()),
});
static SERVER_LICENSE_REQUEST: LazyLock<LicensePdu> = 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<ServerCertificate> = 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<Scope> = LazyLock::new(|| Scope(String::from("microsoft.com")));
static CERT_CHAIN: LazyLock<X509CertificateChain> = 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() {

View file

@ -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<LicensePdu> = 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() {

View file

@ -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<LicenseInformation> = 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<LicensePdu> = 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() {

View file

@ -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<LicenseHeader> = 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() {

View file

@ -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<LogonInfoVersion1> = 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<LogonInfoVersion2> = 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<LogonInfoExtended> = 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<SaveSessionInfoPdu> = LazyLock::new(|| SaveSessionInfoPdu {
info_type: InfoType::PlainNotify,
info_data: InfoData::PlainNotify,
});
#[test]
fn from_buffer_correct_parses_logon_info_v1() {

View file

@ -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
}

View file

@ -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<ChannelPduHeader> = LazyLock::new(|| ChannelPduHeader {
length: CHANNEL_CHUNK_LENGTH_DEFAULT,
flags: ChannelControlFlags::FLAG_FIRST,
});
#[test]
fn from_buffer_correct_parses_channel_header() {

View file

@ -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;

View file

@ -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<ClientNameRequestUnicodeFlag> for CharacterSet {
}
impl From<ClientNameRequestUnicodeFlag> 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<CapabilityType> 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<DeviceType> 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<u32> for MajorFunction {
}
impl From<MajorFunction> 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<MinorFunction> for u32 {
}
}
impl From<MinorFunction> 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

View file

@ -574,6 +574,10 @@ impl ReturnCode {
}
impl From<ReturnCode> 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<u8>,
}
@ -1255,7 +1259,7 @@ impl ndr::Decode for SCardIORequest {
{
ensure_size!(in: src, size: size_of::<u32>() * 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<CharacterSet>) -> 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<CardState> 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
}

View file

@ -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<String> {
const ALIGNMENT: usize = 4;
ensure_size!(ctx: "ndr::read_string_from_cursor", in: cursor, size: size_of::<u32>() * 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::<u16>());
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)

View file

@ -244,6 +244,10 @@ impl TryFrom<u8> for Endianness {
}
impl From<Endianness> 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
}

View file

@ -374,6 +374,10 @@ impl TryFrom<u16> for Component {
}
impl From<Component> 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<PacketId> 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
}

View file

@ -54,7 +54,7 @@ fn main() -> anyhow::Result<()> {
}
});
stream.stream.play()?;
stream.stream().play()?;
thread::sleep(Duration::from_secs(3));
let _ = producer.join();

View file

@ -134,7 +134,7 @@ impl RdpsndClientHandler for RdpsndBackend {
#[doc(hidden)]
pub struct DecodeStream {
_dec_thread: Option<JoinHandle<()>>,
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::<i16>()];
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 {

View file

@ -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());

View file

@ -51,6 +51,10 @@ impl TryFrom<u16> for Version {
}
impl From<Version> 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<u16> for QualityMode {
}
impl From<QualityMode> 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<Self> {
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<Self> {
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,

View file

@ -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)],
}
}

View file

@ -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,

View file

@ -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)
}
}

View file

@ -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<SynchronizeFlags> for KeyboardEvent {
}
impl From<SyncToggleFlags> 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))
}

View file

@ -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 {

View file

@ -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);

View file

@ -110,7 +110,7 @@ pub trait SessionResultExt {
impl<T> SessionResultExt for SessionResult<T> {
fn with_context(self, context: &'static str) -> Self {
self.map_err(|mut e| {
e.context = context;
e.set_context(context);
e
})
}

View file

@ -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],
}
}
}

View file

@ -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 }

View file

@ -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<ServerDemandActive> = 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<ClientConfirmActive> =
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<ClientConfirmActive> = 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()),
],
},
});

View file

@ -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<ClientInfo> = 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<ClientInfo> = 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<ClientInfo> = 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<u8> = {
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<Vec<u8>> = LazyLock::new(|| {
let mut buffer = CLIENT_INFO_BUFFER_UNICODE.to_vec();
buffer.truncate(CLIENT_INFO_BUFFER_UNICODE_WITHOUT_OPTIONAL_FIELDS_LEN);
buffer
});

View file

@ -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<ClientClusterData> = LazyLock::new(|| ClientClusterData {
flags: RedirectionFlags::REDIRECTION_SUPPORTED,
redirection_version: RedirectionVersion::V4,
redirected_session_id: 0,
});

Some files were not shown because too many files have changed in this diff Show more