mirror of
https://github.com/Devolutions/IronRDP.git
synced 2025-12-23 12:26:46 +00:00
Compare commits
36 commits
npm-iron-r
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87f8d073c8 | ||
|
|
bd2aed7686 | ||
|
|
b50b648344 | ||
|
|
113284a053 | ||
|
|
d587b0c4c1 | ||
|
|
0903c9ae75 | ||
|
|
f31af061b9 | ||
|
|
7123150b63 | ||
|
|
632ad86f67 | ||
|
|
da5db5bf11 | ||
|
|
a2af587e60 | ||
|
|
924330159a | ||
|
|
cf978321d3 | ||
|
|
b303ae3a90 | ||
|
|
bca6d190a8 | ||
|
|
cca323adab | ||
|
|
742607240c | ||
|
|
866a6d8674 | ||
|
|
430f70b43f | ||
|
|
5bd319126d | ||
|
|
bfb0cae2f8 | ||
|
|
a70e01d9c5 | ||
|
|
f2326ef046 | ||
|
|
966ba8a53e | ||
|
|
79e71c4f90 | ||
|
|
9622619e8c | ||
|
|
d3e0cb17e1 | ||
|
|
2cedc05722 | ||
|
|
abc391c134 | ||
|
|
bbdecc2aa9 | ||
|
|
e87048c19b | ||
|
|
52225cca3e | ||
|
|
6214c95c6f | ||
|
|
a0a3e750c9 | ||
|
|
d24dbb1e2c | ||
|
|
a24a1fa9e8 |
209 changed files with 6548 additions and 3490 deletions
30
.github/workflows/ci.yml
vendored
30
.github/workflows/ci.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
12
.github/workflows/coverage.yml
vendored
12
.github/workflows/coverage.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
18
.github/workflows/fuzz.yml
vendored
18
.github/workflows/fuzz.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
52
.github/workflows/npm-publish.yml
vendored
52
.github/workflows/npm-publish.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
85
.github/workflows/nuget-publish.yml
vendored
85
.github/workflows/nuget-publish.yml
vendored
|
|
@ -26,7 +26,6 @@ jobs:
|
|||
|
||||
- name: Get dry run
|
||||
id: get-dry-run
|
||||
shell: pwsh
|
||||
run: |
|
||||
$IsDryRun = '${{ github.event.inputs.dry-run }}' -Eq 'true' -Or '${{ github.event_name }}' -Eq 'schedule'
|
||||
|
||||
|
|
@ -35,26 +34,27 @@ jobs:
|
|||
} else {
|
||||
echo "dry-run=false" >> $Env:GITHUB_OUTPUT
|
||||
}
|
||||
shell: pwsh
|
||||
|
||||
- name: Get version
|
||||
id: get-version
|
||||
shell: pwsh
|
||||
run: |
|
||||
$CsprojXml = [Xml] (Get-Content .\ffi\dotnet\Devolutions.IronRdp\Devolutions.IronRdp.csproj)
|
||||
$ProjectVersion = $CsprojXml.Project.PropertyGroup.Version | Select-Object -First 1
|
||||
$PackageVersion = $ProjectVersion -Replace "^(\d+)\.(\d+)\.(\d+).(\d+)$", "`$1.`$2.`$3"
|
||||
echo "project-version=$ProjectVersion" >> $Env:GITHUB_OUTPUT
|
||||
echo "package-version=$PackageVersion" >> $Env:GITHUB_OUTPUT
|
||||
shell: pwsh
|
||||
|
||||
build-native:
|
||||
name: Native build
|
||||
needs: [preflight]
|
||||
runs-on: ${{matrix.runner}}
|
||||
needs: preflight
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ win, osx, linux, ios, android ]
|
||||
arch: [ x86, x64, arm, arm64 ]
|
||||
os: [win, osx, linux, ios, android]
|
||||
arch: [x86, x64, arm, arm64]
|
||||
include:
|
||||
- os: win
|
||||
runner: windows-2022
|
||||
|
|
@ -89,20 +89,20 @@ jobs:
|
|||
uses: actions/checkout@v4
|
||||
|
||||
- name: Configure Android NDK
|
||||
if: ${{ matrix.os == 'android' }}
|
||||
uses: Devolutions/actions-public/cargo-android-ndk@v1
|
||||
if: matrix.os == 'android'
|
||||
with:
|
||||
android_api_level: "21"
|
||||
|
||||
- name: Configure macOS deployement target
|
||||
if: ${{ matrix.os == 'osx' }}
|
||||
shell: pwsh
|
||||
run: Write-Output "MACOSX_DEPLOYMENT_TARGET=10.10" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
|
||||
shell: pwsh
|
||||
|
||||
- name: Configure iOS deployement target
|
||||
if: ${{ matrix.os == 'ios' }}
|
||||
shell: pwsh
|
||||
run: Write-Output "IPHONEOS_DEPLOYMENT_TARGET=12.1" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
|
||||
shell: pwsh
|
||||
|
||||
- name: Update runner
|
||||
if: ${{ matrix.os == 'linux' }}
|
||||
|
|
@ -110,12 +110,11 @@ jobs:
|
|||
|
||||
- name: Install dependencies for rustls
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
shell: pwsh
|
||||
run: |
|
||||
choco install ninja nasm
|
||||
|
||||
# We need to add the NASM binary folder to the PATH manually.
|
||||
# We don’t need to do that for ninja.
|
||||
# We don't need to do that for ninja.
|
||||
Write-Output "PATH=$Env:PATH;$Env:ProgramFiles\NASM" >> $Env:GITHUB_ENV
|
||||
|
||||
# libclang / LLVM is a requirement for AWS LC.
|
||||
|
|
@ -125,6 +124,7 @@ jobs:
|
|||
|
||||
# Install Visual Studio Developer PowerShell Module for cmdlets such as Enter-VsDevShell
|
||||
Install-Module VsDevShell -Force
|
||||
shell: pwsh
|
||||
|
||||
# No pre-generated bindings for Android and iOS.
|
||||
# https://aws.github.io/aws-lc-rs/platform_support.html#pre-generated-bindings
|
||||
|
|
@ -141,19 +141,18 @@ jobs:
|
|||
sudo apt-get install gcc-multilib
|
||||
|
||||
- name: Setup LLVM
|
||||
if: ${{ matrix.os == 'linux' }}
|
||||
uses: Devolutions/actions-public/setup-llvm@v1
|
||||
if: matrix.os == 'linux'
|
||||
with:
|
||||
version: "18.1.8"
|
||||
|
||||
- name: Setup CBake
|
||||
if: ${{ matrix.os == 'linux' }}
|
||||
uses: Devolutions/actions-public/setup-cbake@v1
|
||||
if: matrix.os == 'linux'
|
||||
with:
|
||||
cargo_env_scripts: true
|
||||
|
||||
- name: Build native lib (${{matrix.os}}-${{matrix.arch}})
|
||||
shell: pwsh
|
||||
run: |
|
||||
$DotNetOs = '${{matrix.os}}'
|
||||
$DotNetArch = '${{matrix.arch}}'
|
||||
|
|
@ -210,6 +209,7 @@ jobs:
|
|||
$OutputPath = Join-Path "dependencies" "runtimes" $DotNetRid "native"
|
||||
New-Item -ItemType Directory -Path $OutputPath | Out-Null
|
||||
Copy-Item $OutputLibrary $(Join-Path $OutputPath $RenamedLibraryName)
|
||||
shell: pwsh
|
||||
|
||||
- name: Upload native components
|
||||
uses: actions/upload-artifact@v4
|
||||
|
|
@ -219,8 +219,8 @@ jobs:
|
|||
|
||||
build-universal:
|
||||
name: Universal build
|
||||
needs: [preflight, build-native]
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [ preflight, build-native ]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
|
|
@ -239,7 +239,6 @@ jobs:
|
|||
path: dependencies/runtimes
|
||||
|
||||
- name: Lipo native components
|
||||
shell: pwsh
|
||||
run: |
|
||||
Set-Location "dependencies/runtimes"
|
||||
# No RID for universal binaries, see: https://github.com/dotnet/runtime/issues/53156
|
||||
|
|
@ -249,9 +248,9 @@ jobs:
|
|||
$LipoCmd = $(@('lipo', '-create', '-output', (Join-Path -Path $OutputPath -ChildPath "libDevolutionsIronRdp.dylib")) + $Libraries) -Join ' '
|
||||
Write-Host $LipoCmd
|
||||
Invoke-Expression $LipoCmd
|
||||
shell: pwsh
|
||||
|
||||
- name: Framework
|
||||
shell: pwsh
|
||||
if: ${{ matrix.os == 'ios' }}
|
||||
run: |
|
||||
$Version = '${{ needs.preflight.outputs.project-version }}'
|
||||
|
|
@ -269,19 +268,19 @@ jobs:
|
|||
|
||||
[xml] $InfoPlistXml = Get-Content (Join-Path "ffi" "dotnet" "Devolutions.IronRdp" "Info.plist")
|
||||
Select-Xml -xml $InfoPlistXml -XPath "/plist/dict/key[. = 'CFBundleIdentifier']/following-sibling::string[1]" |
|
||||
%{
|
||||
%{
|
||||
$_.Node.InnerXml = "com.devolutions.ironrdp"
|
||||
}
|
||||
Select-Xml -xml $InfoPlistXml -XPath "/plist/dict/key[. = 'CFBundleExecutable']/following-sibling::string[1]" |
|
||||
%{
|
||||
%{
|
||||
$_.Node.InnerXml = $BundleName
|
||||
}
|
||||
Select-Xml -xml $InfoPlistXml -XPath "/plist/dict/key[. = 'CFBundleVersion']/following-sibling::string[1]" |
|
||||
%{
|
||||
%{
|
||||
$_.Node.InnerXml = $Version
|
||||
}
|
||||
Select-Xml -xml $InfoPlistXml -XPath "/plist/dict/key[. = 'CFBundleShortVersionString']/following-sibling::string[1]" |
|
||||
%{
|
||||
%{
|
||||
$_.Node.InnerXml = $ShortVersion
|
||||
}
|
||||
|
||||
|
|
@ -294,6 +293,7 @@ jobs:
|
|||
# .NET XML document inserts two square brackets at the end of the DOCTYPE tag
|
||||
# It's perfectly valid XML, but we're dealing with plists here and dyld will not be able to read the file
|
||||
((Get-Content -Path (Join-Path $FrameworkDir "Info.plist") -Raw) -Replace 'PropertyList-1.0.dtd"\[\]', 'PropertyList-1.0.dtd"') | Set-Content -Path (Join-Path $FrameworkDir "Info.plist")
|
||||
shell: pwsh
|
||||
|
||||
- name: Upload native components
|
||||
uses: actions/upload-artifact@v4
|
||||
|
|
@ -303,8 +303,8 @@ jobs:
|
|||
|
||||
build-managed:
|
||||
name: Managed build
|
||||
needs: [build-universal]
|
||||
runs-on: windows-2022
|
||||
needs: build-universal
|
||||
|
||||
steps:
|
||||
- name: Check out ${{ github.repository }}
|
||||
|
|
@ -317,9 +317,9 @@ jobs:
|
|||
run: dotnet workload install ios
|
||||
|
||||
- name: Prepare dependencies
|
||||
shell: pwsh
|
||||
run: |
|
||||
New-Item -ItemType Directory -Path "dependencies/runtimes" | Out-Null
|
||||
shell: pwsh
|
||||
|
||||
- name: Download native components
|
||||
uses: actions/download-artifact@v4
|
||||
|
|
@ -327,19 +327,19 @@ jobs:
|
|||
path: dependencies/runtimes
|
||||
|
||||
- name: Rename dependencies
|
||||
shell: pwsh
|
||||
run: |
|
||||
Set-Location "dependencies/runtimes"
|
||||
$(Get-Item ".\ironrdp-*") | ForEach-Object { Rename-Item $_ $_.Name.Replace("ironrdp-", "") }
|
||||
Get-ChildItem * -Recurse
|
||||
shell: pwsh
|
||||
|
||||
- name: Build Devolutions.IronRdp (managed)
|
||||
shell: pwsh
|
||||
run: |
|
||||
# net8.0 target packaged as Devolutions.IronRdp
|
||||
dotnet build .\ffi\dotnet\Devolutions.IronRdp\Devolutions.IronRdp.csproj -c Release
|
||||
# net8.0-ios target packaged as Devolutions.IronRdp.iOS
|
||||
# net9.0-ios target packaged as Devolutions.IronRdp.iOS
|
||||
dotnet build .\ffi\dotnet\Devolutions.IronRdp\Devolutions.IronRdp.csproj -c Release /p:PackageId=Devolutions.IronRdp.iOS
|
||||
shell: pwsh
|
||||
|
||||
- name: Upload managed components
|
||||
uses: actions/upload-artifact@v4
|
||||
|
|
@ -349,12 +349,12 @@ jobs:
|
|||
|
||||
publish:
|
||||
name: Publish NuGet package
|
||||
runs-on: ubuntu-latest
|
||||
environment: nuget-publish
|
||||
if: needs.preflight.outputs.dry-run == 'false'
|
||||
needs:
|
||||
- preflight
|
||||
- build-managed
|
||||
if: ${{ needs.preflight.outputs.dry-run == 'false' }}
|
||||
needs: [preflight, build-managed]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Download NuGet package artifact
|
||||
|
|
@ -363,19 +363,24 @@ jobs:
|
|||
name: ironrdp-nupkg
|
||||
path: package
|
||||
|
||||
- name: NuGet login (OIDC)
|
||||
uses: NuGet/login@v1
|
||||
id: nuget-login
|
||||
with:
|
||||
user: ${{ secrets.NUGET_BOT_USERNAME }}
|
||||
|
||||
- name: Publish to nuget.org
|
||||
shell: pwsh
|
||||
run: |
|
||||
$Files = Get-ChildItem -Recurse package/*.nupkg
|
||||
|
||||
foreach ($File in $Files) {
|
||||
$PushCmd = @(
|
||||
'dotnet',
|
||||
'nuget',
|
||||
'push',
|
||||
'dotnet',
|
||||
'nuget',
|
||||
'push',
|
||||
"$File",
|
||||
'--api-key',
|
||||
'${{ secrets.NUGET_API_KEY }}',
|
||||
'${{ steps.nuget-login.outputs.NUGET_API_KEY }}',
|
||||
'--source',
|
||||
'https://api.nuget.org/v3/index.json',
|
||||
'--skip-duplicate'
|
||||
|
|
@ -385,19 +390,17 @@ jobs:
|
|||
$PushCmd = $PushCmd -Join ' '
|
||||
Invoke-Expression $PushCmd
|
||||
}
|
||||
shell: pwsh
|
||||
|
||||
notify:
|
||||
name: Notify failure
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ always() && contains(needs.*.result, 'failure') && github.event_name == 'schedule' }}
|
||||
needs:
|
||||
- preflight
|
||||
- build-native
|
||||
- build-universal
|
||||
- build-managed
|
||||
needs: [preflight, build-native, build-universal, build-managed]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_ARCHITECTURE }}
|
||||
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
|
||||
|
||||
steps:
|
||||
- name: Send slack notification
|
||||
id: slack
|
||||
|
|
|
|||
15
.github/workflows/release-crates.yml
vendored
15
.github/workflows/release-crates.yml
vendored
|
|
@ -14,9 +14,8 @@ jobs:
|
|||
# Create a PR with the new versions and changelog, preparing the next release.
|
||||
open-pr:
|
||||
name: Open release PR
|
||||
runs-on: ubuntu-latest
|
||||
environment: cratesio-publish
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: release-plz-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
|
@ -37,7 +36,6 @@ jobs:
|
|||
github-token: ${{ secrets.DEVOLUTIONSBOT_WRITE_TOKEN }}
|
||||
|
||||
- name: Update fuzz/Cargo.lock
|
||||
shell: pwsh
|
||||
if: ${{ steps.release-plz.outputs.did-open-pr == 'true' }}
|
||||
run: |
|
||||
$prRaw = '${{ steps.release-plz.outputs.pr }}'
|
||||
|
|
@ -61,12 +59,15 @@ jobs:
|
|||
|
||||
Write-Host "Update the release pull request"
|
||||
git push --force
|
||||
shell: pwsh
|
||||
|
||||
# Release unpublished packages.
|
||||
release:
|
||||
name: Release crates
|
||||
runs-on: ubuntu-latest
|
||||
environment: cratesio-publish
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
|
|
@ -74,8 +75,12 @@ jobs:
|
|||
with:
|
||||
fetch-depth: 512
|
||||
|
||||
- name: Authenticate with crates.io
|
||||
id: auth
|
||||
uses: rust-lang/crates-io-auth-action@v1
|
||||
|
||||
- name: Run release-plz
|
||||
uses: Devolutions/actions-public/release-plz@v1
|
||||
with:
|
||||
command: release
|
||||
registry-token: ${{ secrets.CRATES_IO_TOKEN }}
|
||||
registry-token: ${{ steps.auth.outputs.token }}
|
||||
|
|
|
|||
2029
Cargo.lock
generated
2029
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -34,12 +34,11 @@ categories = ["network-programming"]
|
|||
# even for private dependencies.
|
||||
expect-test = "1"
|
||||
proptest = "1.4"
|
||||
rstest = "0.25"
|
||||
rstest = "0.26"
|
||||
|
||||
# Note: we are trying to move away from using these crates.
|
||||
# They are being kept around for now for legacy compatibility,
|
||||
# but new usage should be avoided.
|
||||
lazy_static = "1.4" # Legacy crate; prefer std::sync::LazyLock or LazyCell
|
||||
num-derive = "0.4"
|
||||
num-traits = "0.2"
|
||||
|
||||
|
|
@ -93,7 +92,7 @@ fn_to_numeric_cast_any = "warn"
|
|||
ptr_cast_constness = "warn"
|
||||
|
||||
# == Correctness == #
|
||||
#as_conversions = "warn"
|
||||
as_conversions = "warn"
|
||||
cast_lossless = "warn"
|
||||
cast_possible_truncation = "warn"
|
||||
cast_possible_wrap = "warn"
|
||||
|
|
@ -128,6 +127,7 @@ empty_enum_variants_with_brackets = "warn"
|
|||
deref_by_slicing = "warn"
|
||||
multiple_inherent_impl = "warn"
|
||||
map_with_unused_argument_over_ranges = "warn"
|
||||
partial_pub_fields = "warn"
|
||||
trait_duplication_in_bounds = "warn"
|
||||
type_repetition_in_bounds = "warn"
|
||||
checked_conversions = "warn"
|
||||
|
|
@ -156,6 +156,7 @@ or_fun_call = "warn"
|
|||
rc_buffer = "warn"
|
||||
string_lit_chars_any = "warn"
|
||||
unnecessary_box_returns = "warn"
|
||||
large_futures = "warn"
|
||||
|
||||
# == Extra-pedantic clippy == #
|
||||
allow_attributes = "warn"
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ qoiz = ["ironrdp/qoiz"]
|
|||
[dependencies]
|
||||
anyhow = "1.0.99"
|
||||
async-trait = "0.1.89"
|
||||
bytesize = "2.1.0"
|
||||
bytesize = "2.3"
|
||||
ironrdp = { path = "../crates/ironrdp", features = [
|
||||
"server",
|
||||
"pdu",
|
||||
|
|
|
|||
|
|
@ -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:?}");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
|
||||
## [[0.8.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-acceptor-v0.7.0...ironrdp-acceptor-v0.8.0)] - 2025-12-18
|
||||
|
||||
### <!-- 4 -->Bug Fixes
|
||||
|
||||
- [**breaking**] Use static dispatch for NetworkClient trait ([#1043](https://github.com/Devolutions/IronRDP/issues/1043)) ([bca6d190a8](https://github.com/Devolutions/IronRDP/commit/bca6d190a870708468534d224ff225a658767a9a))
|
||||
|
||||
- Rename `AsyncNetworkClient` to `NetworkClient`
|
||||
- Replace dynamic dispatch (`Option<&mut dyn ...>`) with static dispatch
|
||||
using generics (`&mut N where N: NetworkClient`)
|
||||
- Reorder `connect_finalize` parameters for consistency across crates
|
||||
|
||||
## [[0.6.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-acceptor-v0.5.0...ironrdp-acceptor-v0.6.0)] - 2025-07-08
|
||||
|
||||
### <!-- 1 -->Features
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "ironrdp-acceptor"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
readme = "README.md"
|
||||
description = "State machines to drive an RDP connection acceptance sequence"
|
||||
edition.workspace = true
|
||||
|
|
@ -19,8 +19,8 @@ test = false
|
|||
ironrdp-core = { path = "../ironrdp-core", version = "0.1", features = ["alloc"] } # public
|
||||
ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6" } # public
|
||||
ironrdp-svc = { path = "../ironrdp-svc", version = "0.5" } # public
|
||||
ironrdp-connector = { path = "../ironrdp-connector", version = "0.7" } # public
|
||||
ironrdp-async = { path = "../ironrdp-async", version = "0.7" } # public
|
||||
ironrdp-connector = { path = "../ironrdp-connector", version = "0.8" } # public
|
||||
ironrdp-async = { path = "../ironrdp-async", version = "0.8" } # public
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
|
||||
[lints]
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use ironrdp_async::AsyncNetworkClient;
|
||||
use ironrdp_async::NetworkClient;
|
||||
use ironrdp_connector::sspi::credssp::{
|
||||
CredSspServer, CredentialsProxy, ServerError, ServerMode, ServerState, TsRequest,
|
||||
};
|
||||
|
|
@ -71,7 +71,7 @@ impl CredentialsProxy for CredentialsProxyImpl<'_> {
|
|||
|
||||
pub(crate) async fn resolve_generator(
|
||||
generator: &mut CredsspProcessGenerator<'_>,
|
||||
network_client: &mut dyn AsyncNetworkClient,
|
||||
network_client: &mut impl NetworkClient,
|
||||
) -> Result<ServerState, ServerError> {
|
||||
let mut state = generator.start();
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#![cfg_attr(doc, doc = include_str!("../README.md"))]
|
||||
#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")]
|
||||
|
||||
use ironrdp_async::{single_sequence_step, AsyncNetworkClient, Framed, FramedRead, FramedWrite, StreamWrapper};
|
||||
use ironrdp_async::{single_sequence_step, Framed, FramedRead, FramedWrite, NetworkClient, StreamWrapper};
|
||||
use ironrdp_connector::sspi::credssp::EarlyUserAuthResult;
|
||||
use ironrdp_connector::sspi::{AuthIdentity, KerberosServerConfig, Username};
|
||||
use ironrdp_connector::{custom_err, general_err, ConnectorResult, ServerName};
|
||||
|
|
@ -51,16 +51,17 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn accept_credssp<S>(
|
||||
pub async fn accept_credssp<S, N>(
|
||||
framed: &mut Framed<S>,
|
||||
acceptor: &mut Acceptor,
|
||||
network_client: &mut N,
|
||||
client_computer_name: ServerName,
|
||||
public_key: Vec<u8>,
|
||||
kerberos_config: Option<KerberosServerConfig>,
|
||||
network_client: Option<&mut dyn AsyncNetworkClient>,
|
||||
) -> ConnectorResult<()>
|
||||
where
|
||||
S: FramedRead + FramedWrite,
|
||||
N: NetworkClient,
|
||||
{
|
||||
let mut buf = WriteBuf::new();
|
||||
|
||||
|
|
@ -68,11 +69,11 @@ where
|
|||
perform_credssp_step(
|
||||
framed,
|
||||
acceptor,
|
||||
network_client,
|
||||
&mut buf,
|
||||
client_computer_name,
|
||||
public_key,
|
||||
kerberos_config,
|
||||
network_client,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
|
|
@ -98,34 +99,73 @@ where
|
|||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all, ret)]
|
||||
async fn perform_credssp_step<S>(
|
||||
async fn perform_credssp_step<S, N>(
|
||||
framed: &mut Framed<S>,
|
||||
acceptor: &mut Acceptor,
|
||||
network_client: &mut N,
|
||||
buf: &mut WriteBuf,
|
||||
client_computer_name: ServerName,
|
||||
public_key: Vec<u8>,
|
||||
kerberos_config: Option<KerberosServerConfig>,
|
||||
network_client: Option<&mut dyn AsyncNetworkClient>,
|
||||
) -> ConnectorResult<()>
|
||||
where
|
||||
S: FramedRead + FramedWrite,
|
||||
N: NetworkClient,
|
||||
{
|
||||
assert!(acceptor.should_perform_credssp());
|
||||
let AcceptorState::Credssp { protocol, .. } = acceptor.state else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
async fn credssp_loop<S>(
|
||||
let result = credssp_loop(
|
||||
framed,
|
||||
acceptor,
|
||||
network_client,
|
||||
buf,
|
||||
client_computer_name,
|
||||
public_key,
|
||||
kerberos_config,
|
||||
)
|
||||
.await;
|
||||
|
||||
if protocol.intersects(nego::SecurityProtocol::HYBRID_EX) {
|
||||
trace!(?result, "HYBRID_EX");
|
||||
|
||||
let result = if result.is_ok() {
|
||||
EarlyUserAuthResult::Success
|
||||
} else {
|
||||
EarlyUserAuthResult::AccessDenied
|
||||
};
|
||||
|
||||
buf.clear();
|
||||
result
|
||||
.to_buffer(&mut *buf)
|
||||
.map_err(|e| ironrdp_connector::custom_err!("to_buffer", e))?;
|
||||
let response = &buf[..result.buffer_len()];
|
||||
framed
|
||||
.write_all(response)
|
||||
.await
|
||||
.map_err(|e| ironrdp_connector::custom_err!("write all", e))?;
|
||||
}
|
||||
|
||||
result?;
|
||||
|
||||
acceptor.mark_credssp_as_done();
|
||||
|
||||
return Ok(());
|
||||
|
||||
async fn credssp_loop<S, N>(
|
||||
framed: &mut Framed<S>,
|
||||
acceptor: &mut Acceptor,
|
||||
network_client: &mut N,
|
||||
buf: &mut WriteBuf,
|
||||
client_computer_name: ServerName,
|
||||
public_key: Vec<u8>,
|
||||
kerberos_config: Option<KerberosServerConfig>,
|
||||
mut network_client: Option<&mut dyn AsyncNetworkClient>,
|
||||
) -> ConnectorResult<()>
|
||||
where
|
||||
S: FramedRead + FramedWrite,
|
||||
N: NetworkClient,
|
||||
{
|
||||
let creds = acceptor
|
||||
.creds
|
||||
|
|
@ -164,12 +204,7 @@ where
|
|||
|
||||
let result = {
|
||||
let mut generator = sequence.process_ts_request(ts_request);
|
||||
|
||||
if let Some(network_client_ref) = network_client.as_deref_mut() {
|
||||
resolve_generator(&mut generator, network_client_ref).await
|
||||
} else {
|
||||
generator.resolve_to_result()
|
||||
}
|
||||
resolve_generator(&mut generator, network_client).await
|
||||
}; // drop generator
|
||||
|
||||
buf.clear();
|
||||
|
|
@ -184,43 +219,7 @@ where
|
|||
.map_err(|e| ironrdp_connector::custom_err!("write all", e))?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let result = credssp_loop(
|
||||
framed,
|
||||
acceptor,
|
||||
buf,
|
||||
client_computer_name,
|
||||
public_key,
|
||||
kerberos_config,
|
||||
network_client,
|
||||
)
|
||||
.await;
|
||||
|
||||
if protocol.intersects(nego::SecurityProtocol::HYBRID_EX) {
|
||||
trace!(?result, "HYBRID_EX");
|
||||
|
||||
let result = if result.is_ok() {
|
||||
EarlyUserAuthResult::Success
|
||||
} else {
|
||||
EarlyUserAuthResult::AccessDenied
|
||||
};
|
||||
|
||||
buf.clear();
|
||||
result
|
||||
.to_buffer(&mut *buf)
|
||||
.map_err(|e| ironrdp_connector::custom_err!("to_buffer", e))?;
|
||||
let response = &buf[..result.buffer_len()];
|
||||
framed
|
||||
.write_all(response)
|
||||
.await
|
||||
.map_err(|e| ironrdp_connector::custom_err!("write all", e))?;
|
||||
}
|
||||
|
||||
result?;
|
||||
|
||||
acceptor.mark_credssp_as_done();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,14 +6,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
|
||||
## [[0.8.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-async-v0.7.0...ironrdp-async-v0.8.0)] - 2025-12-18
|
||||
|
||||
### <!-- 4 -->Bug Fixes
|
||||
|
||||
- [**breaking**] Use static dispatch for NetworkClient trait ([#1043](https://github.com/Devolutions/IronRDP/issues/1043)) ([bca6d190a8](https://github.com/Devolutions/IronRDP/commit/bca6d190a870708468534d224ff225a658767a9a))
|
||||
|
||||
- Rename `AsyncNetworkClient` to `NetworkClient`
|
||||
- Replace dynamic dispatch (`Option<&mut dyn ...>`) with static dispatch
|
||||
using generics (`&mut N where N: NetworkClient`)
|
||||
- Reorder `connect_finalize` parameters for consistency across crates
|
||||
|
||||
## [[0.3.2](https://github.com/Devolutions/IronRDP/compare/ironrdp-async-v0.3.1...ironrdp-async-v0.3.2)] - 2025-03-12
|
||||
|
||||
### <!-- 7 -->Build
|
||||
|
||||
- Bump ironrdp-pdu
|
||||
|
||||
|
||||
|
||||
## [[0.3.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-async-v0.3.0...ironrdp-async-v0.3.1)] - 2025-03-12
|
||||
|
||||
### <!-- 7 -->Build
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "ironrdp-async"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
readme = "README.md"
|
||||
description = "Provides `Future`s wrapping the IronRDP state machines conveniently"
|
||||
edition.workspace = true
|
||||
|
|
@ -16,7 +16,7 @@ doctest = false
|
|||
test = false
|
||||
|
||||
[dependencies]
|
||||
ironrdp-connector = { path = "../ironrdp-connector", version = "0.7" } # public
|
||||
ironrdp-connector = { path = "../ironrdp-connector", version = "0.8" } # public
|
||||
ironrdp-core = { path = "../ironrdp-core", version = "0.1", features = ["alloc"] } # public
|
||||
ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6" } # public
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ use ironrdp_connector::credssp::{CredsspProcessGenerator, CredsspSequence, Kerbe
|
|||
use ironrdp_connector::sspi::credssp::ClientState;
|
||||
use ironrdp_connector::sspi::generator::GeneratorState;
|
||||
use ironrdp_connector::{
|
||||
custom_err, general_err, ClientConnector, ClientConnectorState, ConnectionResult, ConnectorError, ConnectorResult,
|
||||
ServerName, State as _,
|
||||
general_err, ClientConnector, ClientConnectorState, ConnectionResult, ConnectorError, ConnectorResult, ServerName,
|
||||
State as _,
|
||||
};
|
||||
use ironrdp_core::WriteBuf;
|
||||
use tracing::{debug, info, instrument, trace};
|
||||
|
||||
use crate::framed::{Framed, FramedRead, FramedWrite};
|
||||
use crate::{single_sequence_step, AsyncNetworkClient};
|
||||
use crate::{single_sequence_step, NetworkClient};
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct ShouldUpgrade;
|
||||
|
|
@ -49,28 +49,29 @@ pub fn mark_as_upgraded(_: ShouldUpgrade, connector: &mut ClientConnector) -> Up
|
|||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn connect_finalize<S>(
|
||||
pub async fn connect_finalize<S, N>(
|
||||
_: Upgraded,
|
||||
framed: &mut Framed<S>,
|
||||
mut connector: ClientConnector,
|
||||
framed: &mut Framed<S>,
|
||||
network_client: &mut N,
|
||||
server_name: ServerName,
|
||||
server_public_key: Vec<u8>,
|
||||
network_client: Option<&mut dyn AsyncNetworkClient>,
|
||||
kerberos_config: Option<KerberosConfig>,
|
||||
) -> ConnectorResult<ConnectionResult>
|
||||
where
|
||||
S: FramedRead + FramedWrite,
|
||||
N: NetworkClient,
|
||||
{
|
||||
let mut buf = WriteBuf::new();
|
||||
|
||||
if connector.should_perform_credssp() {
|
||||
perform_credssp_step(
|
||||
framed,
|
||||
&mut connector,
|
||||
framed,
|
||||
network_client,
|
||||
&mut buf,
|
||||
server_name,
|
||||
server_public_key,
|
||||
network_client,
|
||||
kerberos_config,
|
||||
)
|
||||
.await?;
|
||||
|
|
@ -91,7 +92,7 @@ where
|
|||
|
||||
async fn resolve_generator(
|
||||
generator: &mut CredsspProcessGenerator<'_>,
|
||||
network_client: &mut dyn AsyncNetworkClient,
|
||||
network_client: &mut impl NetworkClient,
|
||||
) -> ConnectorResult<ClientState> {
|
||||
let mut state = generator.start();
|
||||
|
||||
|
|
@ -110,17 +111,18 @@ async fn resolve_generator(
|
|||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
async fn perform_credssp_step<S>(
|
||||
framed: &mut Framed<S>,
|
||||
async fn perform_credssp_step<S, N>(
|
||||
connector: &mut ClientConnector,
|
||||
framed: &mut Framed<S>,
|
||||
network_client: &mut N,
|
||||
buf: &mut WriteBuf,
|
||||
server_name: ServerName,
|
||||
server_public_key: Vec<u8>,
|
||||
mut network_client: Option<&mut dyn AsyncNetworkClient>,
|
||||
kerberos_config: Option<KerberosConfig>,
|
||||
) -> ConnectorResult<()>
|
||||
where
|
||||
S: FramedRead + FramedWrite,
|
||||
N: NetworkClient,
|
||||
{
|
||||
assert!(connector.should_perform_credssp());
|
||||
|
||||
|
|
@ -141,15 +143,8 @@ where
|
|||
loop {
|
||||
let client_state = {
|
||||
let mut generator = sequence.process_ts_request(ts_request);
|
||||
|
||||
if let Some(network_client_ref) = network_client.as_deref_mut() {
|
||||
trace!("resolving network");
|
||||
resolve_generator(&mut generator, network_client_ref).await?
|
||||
} else {
|
||||
generator
|
||||
.resolve_to_result()
|
||||
.map_err(|e| custom_err!("resolve without network client", e))?
|
||||
}
|
||||
trace!("resolving network");
|
||||
resolve_generator(&mut generator, network_client).await?
|
||||
}; // drop generator
|
||||
|
||||
buf.clear();
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
#![cfg_attr(doc, doc = include_str!("../README.md"))]
|
||||
#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")]
|
||||
|
||||
use core::future::Future;
|
||||
|
||||
pub use bytes;
|
||||
|
||||
mod connector;
|
||||
mod framed;
|
||||
mod session;
|
||||
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
|
||||
use ironrdp_connector::sspi::generator::NetworkRequest;
|
||||
use ironrdp_connector::ConnectorResult;
|
||||
|
||||
|
|
@ -17,9 +16,6 @@ pub use self::connector::*;
|
|||
pub use self::framed::*;
|
||||
// pub use self::session::*;
|
||||
|
||||
pub trait AsyncNetworkClient {
|
||||
fn send<'a>(
|
||||
&'a mut self,
|
||||
network_request: &'a NetworkRequest,
|
||||
) -> Pin<Box<dyn Future<Output = ConnectorResult<Vec<u8>>> + 'a>>;
|
||||
pub trait NetworkClient {
|
||||
fn send(&mut self, network_request: &NetworkRequest) -> impl Future<Output = ConnectorResult<Vec<u8>>>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ edition.workspace = true
|
|||
publish = false
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.7"
|
||||
criterion = "0.8"
|
||||
ironrdp-graphics.path = "../ironrdp-graphics"
|
||||
ironrdp-pdu.path = "../ironrdp-pdu"
|
||||
ironrdp-server = { path = "../ironrdp-server", features = ["__bench"] }
|
||||
|
|
|
|||
|
|
@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
|
||||
## [[0.8.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-blocking-v0.7.0...ironrdp-blocking-v0.8.0)] - 2025-12-18
|
||||
|
||||
### <!-- 4 -->Bug Fixes
|
||||
|
||||
- [**breaking**] Use static dispatch for NetworkClient trait ([#1043](https://github.com/Devolutions/IronRDP/issues/1043)) ([bca6d190a8](https://github.com/Devolutions/IronRDP/commit/bca6d190a870708468534d224ff225a658767a9a))
|
||||
|
||||
- Rename `AsyncNetworkClient` to `NetworkClient`
|
||||
- Replace dynamic dispatch (`Option<&mut dyn ...>`) with static dispatch
|
||||
using generics (`&mut N where N: NetworkClient`)
|
||||
- Reorder `connect_finalize` parameters for consistency across crates
|
||||
|
||||
## [[0.4.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-blocking-v0.3.1...ironrdp-blocking-v0.4.0)] - 2025-03-12
|
||||
|
||||
### <!-- 7 -->Build
|
||||
|
|
@ -13,7 +24,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Bump ironrdp-pdu
|
||||
|
||||
|
||||
|
||||
## [[0.3.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-blocking-v0.3.0...ironrdp-blocking-v0.3.1)] - 2025-03-12
|
||||
|
||||
### <!-- 7 -->Build
|
||||
|
|
@ -31,7 +41,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Use CDN URLs instead of the blob storage URLs for Devolutions logo (#631) ([dd249909a8](https://github.com/Devolutions/IronRDP/commit/dd249909a894004d4f728d30b3a4aa77a0f8193b))
|
||||
|
||||
|
||||
|
||||
## [[0.2.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-blocking-v0.2.0...ironrdp-blocking-v0.2.1)] - 2024-12-14
|
||||
|
||||
### Other
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "ironrdp-blocking"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
readme = "README.md"
|
||||
description = "Blocking I/O abstraction wrapping the IronRDP state machines conveniently"
|
||||
edition.workspace = true
|
||||
|
|
@ -16,7 +16,7 @@ doctest = false
|
|||
test = false
|
||||
|
||||
[dependencies]
|
||||
ironrdp-connector = { path = "../ironrdp-connector", version = "0.7" } # public
|
||||
ironrdp-connector = { path = "../ironrdp-connector", version = "0.8" } # public
|
||||
ironrdp-core = { path = "../ironrdp-core", version = "0.1", features = ["alloc"] } # public
|
||||
ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6" } # public
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
|
|
|
|||
|
|
@ -53,11 +53,11 @@ pub fn mark_as_upgraded(_: ShouldUpgrade, connector: &mut ClientConnector) -> Up
|
|||
#[instrument(skip_all)]
|
||||
pub fn connect_finalize<S>(
|
||||
_: Upgraded,
|
||||
framed: &mut Framed<S>,
|
||||
mut connector: ClientConnector,
|
||||
framed: &mut Framed<S>,
|
||||
network_client: &mut impl NetworkClient,
|
||||
server_name: ServerName,
|
||||
server_public_key: Vec<u8>,
|
||||
network_client: &mut impl NetworkClient,
|
||||
kerberos_config: Option<KerberosConfig>,
|
||||
) -> ConnectorResult<ConnectionResult>
|
||||
where
|
||||
|
|
@ -69,12 +69,12 @@ where
|
|||
|
||||
if connector.should_perform_credssp() {
|
||||
perform_credssp_step(
|
||||
framed,
|
||||
&mut connector,
|
||||
framed,
|
||||
network_client,
|
||||
&mut buf,
|
||||
server_name,
|
||||
server_public_key,
|
||||
network_client,
|
||||
kerberos_config,
|
||||
)?;
|
||||
}
|
||||
|
|
@ -118,12 +118,12 @@ fn resolve_generator(
|
|||
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
fn perform_credssp_step<S>(
|
||||
framed: &mut Framed<S>,
|
||||
connector: &mut ClientConnector,
|
||||
framed: &mut Framed<S>,
|
||||
network_client: &mut impl NetworkClient,
|
||||
buf: &mut WriteBuf,
|
||||
server_name: ServerName,
|
||||
server_public_key: Vec<u8>,
|
||||
network_client: &mut impl NetworkClient,
|
||||
kerberos_config: Option<KerberosConfig>,
|
||||
) -> ConnectorResult<()>
|
||||
where
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ qoiz = ["ironrdp/qoiz"]
|
|||
|
||||
[dependencies]
|
||||
# Protocols
|
||||
ironrdp = { path = "../ironrdp", version = "0.13", features = [
|
||||
ironrdp = { path = "../ironrdp", version = "0.14", features = [
|
||||
"session",
|
||||
"input",
|
||||
"graphics",
|
||||
|
|
@ -45,11 +45,11 @@ ironrdp = { path = "../ironrdp", version = "0.13", features = [
|
|||
"connector",
|
||||
] }
|
||||
ironrdp-core = { path = "../ironrdp-core", version = "0.1", features = ["alloc"] }
|
||||
ironrdp-cliprdr-native = { path = "../ironrdp-cliprdr-native", version = "0.4" }
|
||||
ironrdp-cliprdr-native = { path = "../ironrdp-cliprdr-native", version = "0.5" }
|
||||
ironrdp-rdpsnd-native = { path = "../ironrdp-rdpsnd-native", version = "0.4" }
|
||||
ironrdp-tls = { path = "../ironrdp-tls", version = "0.1" }
|
||||
ironrdp-tls = { path = "../ironrdp-tls", version = "0.2" }
|
||||
ironrdp-mstsgu = { path = "../ironrdp-mstsgu" }
|
||||
ironrdp-tokio = { path = "../ironrdp-tokio", version = "0.7", features = ["reqwest"] }
|
||||
ironrdp-tokio = { path = "../ironrdp-tokio", version = "0.8", features = ["reqwest"] }
|
||||
ironrdp-rdcleanpath.path = "../ironrdp-rdcleanpath"
|
||||
ironrdp-dvc-pipe-proxy.path = "../ironrdp-dvc-pipe-proxy"
|
||||
ironrdp-propertyset.path = "../ironrdp-propertyset"
|
||||
|
|
@ -72,7 +72,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
|||
# Async, futures
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-util = { version = "0.7" }
|
||||
tokio-tungstenite = "0.27"
|
||||
tokio-tungstenite = "0.28"
|
||||
transport = { git = "https://github.com/Devolutions/devolutions-gateway", rev = "06e91dfe82751a6502eaf74b6a99663f06f0236d" }
|
||||
futures-util = { version = "0.3", features = ["sink"] }
|
||||
|
||||
|
|
@ -83,12 +83,12 @@ smallvec = "1.15"
|
|||
tap = "1"
|
||||
semver = "1"
|
||||
raw-window-handle = "0.6"
|
||||
uuid = { version = "1.18" }
|
||||
uuid = { version = "1.19" }
|
||||
x509-cert = { version = "0.2", default-features = false, features = ["std"] }
|
||||
url = "2"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows = { version = "0.61", features = ["Win32_Foundation"] }
|
||||
windows = { version = "0.62", features = ["Win32_Foundation"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
));
|
||||
|
|
|
|||
|
|
@ -229,22 +229,24 @@ async fn connect(
|
|||
// Ensure there is no leftover
|
||||
let (initial_stream, leftover_bytes) = framed.into_inner();
|
||||
|
||||
let (upgraded_stream, server_public_key) = ironrdp_tls::upgrade(initial_stream, config.destination.name())
|
||||
let (upgraded_stream, tls_cert) = ironrdp_tls::upgrade(initial_stream, config.destination.name())
|
||||
.await
|
||||
.map_err(|e| connector::custom_err!("TLS upgrade", e))?;
|
||||
|
||||
let upgraded = ironrdp_tokio::mark_as_upgraded(should_upgrade, &mut connector);
|
||||
|
||||
let erased_stream = Box::new(upgraded_stream) as Box<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 server_public_key = ironrdp_tls::extract_tls_server_public_key(&tls_cert)
|
||||
.ok_or_else(|| connector::general_err!("unable to extract tls server public key"))?;
|
||||
let connection_result = ironrdp_tokio::connect_finalize(
|
||||
upgraded,
|
||||
&mut upgraded_framed,
|
||||
connector,
|
||||
&mut upgraded_framed,
|
||||
&mut ReqwestNetworkClient::new(),
|
||||
(&config.destination).into(),
|
||||
server_public_key,
|
||||
Some(&mut ReqwestNetworkClient::new()),
|
||||
server_public_key.to_owned(),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
|
@ -326,17 +328,17 @@ async fn connect_ws(
|
|||
|
||||
let connection_result = ironrdp_tokio::connect_finalize(
|
||||
upgraded,
|
||||
&mut framed,
|
||||
connector,
|
||||
&mut framed,
|
||||
&mut ReqwestNetworkClient::new(),
|
||||
(&config.destination).into(),
|
||||
server_public_key,
|
||||
Some(&mut ReqwestNetworkClient::new()),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let (ws, leftover_bytes) = framed.into_inner();
|
||||
let erased_stream = Box::new(ws) as Box<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 +662,7 @@ async fn active_session(
|
|||
desktop_size,
|
||||
enable_server_pointer,
|
||||
pointer_software_rendering,
|
||||
} = connection_activation.state
|
||||
} = connection_activation.connection_activation_state()
|
||||
{
|
||||
debug!(?desktop_size, "Deactivation-Reactivation Sequence completed");
|
||||
// Update image size with the new desktop size.
|
||||
|
|
|
|||
|
|
@ -6,6 +6,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
|
||||
## [[0.5.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-cliprdr-native-v0.4.0...ironrdp-cliprdr-native-v0.5.0)] - 2025-12-18
|
||||
|
||||
### <!-- 4 -->Bug Fixes
|
||||
|
||||
- Prevent window class registration error on multiple sessions ([#1047](https://github.com/Devolutions/IronRDP/issues/1047)) ([a2af587e60](https://github.com/Devolutions/IronRDP/commit/a2af587e60e869f0235703e21772d1fc6a7dadcd))
|
||||
|
||||
When starting a second clipboard session, `RegisterClassA` would fail
|
||||
with `ERROR_CLASS_ALREADY_EXISTS` because window classes are global to
|
||||
the process. Now checks if the class is already registered before
|
||||
attempting registration, allowing multiple WinClipboard instances to
|
||||
coexist.
|
||||
|
||||
### <!-- 7 -->Build
|
||||
|
||||
- Bump windows from 0.61.3 to 0.62.1 ([#1010](https://github.com/Devolutions/IronRDP/issues/1010)) ([79e71c4f90](https://github.com/Devolutions/IronRDP/commit/79e71c4f90ea68b14fe45241c1cf3953027b22a2))
|
||||
|
||||
## [[0.4.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-cliprdr-native-v0.3.0...ironrdp-cliprdr-native-v0.4.0)] - 2025-08-29
|
||||
|
||||
### <!-- 4 -->Bug Fixes
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "ironrdp-cliprdr-native"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
readme = "README.md"
|
||||
description = "Native CLIPRDR static channel backend implementations for IronRDP"
|
||||
edition.workspace = true
|
||||
|
|
@ -16,12 +16,12 @@ doctest = false
|
|||
test = false
|
||||
|
||||
[dependencies]
|
||||
ironrdp-cliprdr = { path = "../ironrdp-cliprdr", version = "0.4" } # public
|
||||
ironrdp-cliprdr = { path = "../ironrdp-cliprdr", version = "0.5" } # public
|
||||
ironrdp-core = { path = "../ironrdp-core", version = "0.1" }
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows = { version = "0.61", features = [
|
||||
windows = { version = "0.62", features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Graphics_Gdi",
|
||||
"Win32_System_DataExchange",
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ use windows::Win32::System::DataExchange::{AddClipboardFormatListener, RemoveCli
|
|||
use windows::Win32::System::LibraryLoader::GetModuleHandleA;
|
||||
use windows::Win32::UI::Shell::{RemoveWindowSubclass, SetWindowSubclass};
|
||||
use windows::Win32::UI::WindowsAndMessaging::{
|
||||
CreateWindowExA, DefWindowProcA, RegisterClassA, CW_USEDEFAULT, WINDOW_EX_STYLE, WM_USER, WNDCLASSA, WS_POPUP,
|
||||
CreateWindowExA, DefWindowProcA, GetClassInfoA, RegisterClassA, CW_USEDEFAULT, WINDOW_EX_STYLE, WM_USER, WNDCLASSA,
|
||||
WS_POPUP,
|
||||
};
|
||||
|
||||
use self::clipboard_impl::{clipboard_subproc, WinClipboardImpl};
|
||||
|
|
@ -152,17 +153,25 @@ impl WinClipboard {
|
|||
// SAFETY: low-level WinAPI call
|
||||
let instance = unsafe { GetModuleHandleA(None)? };
|
||||
let window_class = s!("IronRDPClipboardMonitor");
|
||||
let wc = WNDCLASSA {
|
||||
hInstance: instance.into(),
|
||||
lpszClassName: window_class,
|
||||
lpfnWndProc: Some(wndproc),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// SAFETY: low-level WinAPI call
|
||||
let atom = unsafe { RegisterClassA(&wc) };
|
||||
if atom == 0 {
|
||||
return Err(WinCliprdrError::from(Error::from_win32()));
|
||||
let mut existing_wc = WNDCLASSA::default();
|
||||
// SAFETY: `instance` is a valid module handle, `window_class` is a valid null-terminated string,
|
||||
// and `existing_wc` is a valid mutable reference to a WNDCLASSA structure.
|
||||
let class_exists = unsafe { GetClassInfoA(Some(instance.into()), window_class, &mut existing_wc).is_ok() };
|
||||
|
||||
if !class_exists {
|
||||
let wc = WNDCLASSA {
|
||||
hInstance: instance.into(),
|
||||
lpszClassName: window_class,
|
||||
lpfnWndProc: Some(wndproc),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// SAFETY: low-level WinAPI call
|
||||
let atom = unsafe { RegisterClassA(&wc) };
|
||||
if atom == 0 {
|
||||
return Err(WinCliprdrError::from(Error::from_thread()));
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: low-level WinAPI call
|
||||
|
|
@ -184,7 +193,7 @@ impl WinClipboard {
|
|||
};
|
||||
|
||||
if window.is_invalid() {
|
||||
return Err(WinCliprdrError::from(Error::from_win32()));
|
||||
return Err(WinCliprdrError::from(Error::from_thread()));
|
||||
}
|
||||
// Init clipboard processing for WinAPI event loop
|
||||
//
|
||||
|
|
@ -200,8 +209,14 @@ impl WinClipboard {
|
|||
//
|
||||
// SAFETY: `window` is a valid window handle, `clipboard_subproc` is in the static memory,
|
||||
// `ctx` is valid and its ownership is transferred to the subclass via `into_raw`.
|
||||
let winapi_result =
|
||||
unsafe { SetWindowSubclass(window, Some(clipboard_subproc), 0, Box::into_raw(ctx) as usize) };
|
||||
let winapi_result = unsafe {
|
||||
SetWindowSubclass(
|
||||
window,
|
||||
Some(clipboard_subproc),
|
||||
0,
|
||||
Box::into_raw(ctx).expose_provenance(),
|
||||
)
|
||||
};
|
||||
|
||||
if winapi_result == FALSE {
|
||||
return Err(WinCliprdrError::WindowSubclass);
|
||||
|
|
|
|||
|
|
@ -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) } {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,30 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
|
||||
## [[0.5.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-cliprdr-v0.4.0...ironrdp-cliprdr-v0.5.0)] - 2025-12-18
|
||||
|
||||
### <!-- 4 -->Bug Fixes
|
||||
|
||||
- Fixes the Cliprdr `SvcProcessor` impl to support handling a `TemporaryDirectory` Clipboard PDU ([#1031](https://github.com/Devolutions/IronRDP/issues/1031)) ([f2326ef046](https://github.com/Devolutions/IronRDP/commit/f2326ef046cc81fb0e8985f03382859085882e86))
|
||||
|
||||
- Allow servers to announce clipboard ownership ([#1053](https://github.com/Devolutions/IronRDP/issues/1053)) ([d587b0c4c1](https://github.com/Devolutions/IronRDP/commit/d587b0c4c114c49d30f52859f43b22f829456a01))
|
||||
|
||||
Servers can now send Format List PDU via initiate_copy() regardless of
|
||||
internal state. The existing state machine was designed for clients
|
||||
where clipboard initialization must complete before announcing
|
||||
ownership.
|
||||
|
||||
MS-RDPECLIP Section 2.2.3.1 specifies that Format List PDU is sent by
|
||||
either client or server when the local clipboard is updated. Servers
|
||||
should be able to announce clipboard changes immediately after channel
|
||||
negotiation.
|
||||
|
||||
This change enables RDP servers to properly announce clipboard ownership
|
||||
by bypassing the Initialization/Ready state check when R::is_server() is
|
||||
true. Client behavior remains unchanged.
|
||||
|
||||
- [**breaking**] Removed the `PackedMetafile::data()` method in favor of making the `PackedMetafile::data` field public.
|
||||
|
||||
## [[0.4.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-cliprdr-v0.3.0...ironrdp-cliprdr-v0.4.0)] - 2025-08-29
|
||||
|
||||
### <!-- 4 -->Bug Fixes
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "ironrdp-cliprdr"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
readme = "README.md"
|
||||
description = "CLIPRDR static channel for clipboard implemented as described in MS-RDPECLIP"
|
||||
edition.workspace = true
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
#![cfg_attr(doc, doc = include_str!("../README.md"))]
|
||||
#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")]
|
||||
#![allow(clippy::arithmetic_side_effects)] // FIXME: remove
|
||||
#![allow(clippy::cast_lossless)] // FIXME: remove
|
||||
#![allow(clippy::cast_possible_truncation)] // FIXME: remove
|
||||
#![allow(clippy::cast_possible_wrap)] // FIXME: remove
|
||||
#![allow(clippy::cast_sign_loss)] // FIXME: remove
|
||||
|
||||
pub mod backend;
|
||||
pub mod pdu;
|
||||
|
|
@ -32,16 +28,12 @@ pub type CliprdrSvcMessages<R> = SvcProcessorMessages<Cliprdr<R>>;
|
|||
|
||||
#[derive(Debug)]
|
||||
enum ClipboardError {
|
||||
UnimplementedPdu { pdu: &'static str },
|
||||
FormatListRejected,
|
||||
}
|
||||
|
||||
impl core::fmt::Display for ClipboardError {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
ClipboardError::UnimplementedPdu { pdu } => {
|
||||
write!(f, "received clipboard PDU `{pdu}` is not implemented")
|
||||
}
|
||||
ClipboardError::FormatListRejected => write!(f, "sent format list was rejected"),
|
||||
}
|
||||
}
|
||||
|
|
@ -238,26 +230,32 @@ impl<R: Role> Cliprdr<R> {
|
|||
pub fn initiate_copy(&self, available_formats: &[ClipboardFormat]) -> PduResult<CliprdrSvcMessages<R>> {
|
||||
let mut pdus = Vec::new();
|
||||
|
||||
match (self.state, R::is_server()) {
|
||||
// When user initiates copy, we should send format list to server.
|
||||
(CliprdrState::Ready, _) => {
|
||||
pdus.push(ClipboardPdu::FormatList(
|
||||
self.build_format_list(available_formats).map_err(|e| encode_err!(e))?,
|
||||
));
|
||||
}
|
||||
(CliprdrState::Initialization, false) => {
|
||||
// During initialization state, first copy action is synthetic and should be sent along with
|
||||
// capabilities and temporary directory PDUs.
|
||||
pdus.push(ClipboardPdu::Capabilities(self.capabilities.clone()));
|
||||
pdus.push(ClipboardPdu::TemporaryDirectory(
|
||||
ClientTemporaryDirectory::new(self.backend.temporary_directory()).map_err(|e| encode_err!(e))?,
|
||||
));
|
||||
pdus.push(ClipboardPdu::FormatList(
|
||||
self.build_format_list(available_formats).map_err(|e| encode_err!(e))?,
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
error!(?self.state, "Attempted to initiate copy in incorrect state");
|
||||
if R::is_server() {
|
||||
pdus.push(ClipboardPdu::FormatList(
|
||||
self.build_format_list(available_formats).map_err(|e| encode_err!(e))?,
|
||||
));
|
||||
} else {
|
||||
match self.state {
|
||||
CliprdrState::Ready => {
|
||||
pdus.push(ClipboardPdu::FormatList(
|
||||
self.build_format_list(available_formats).map_err(|e| encode_err!(e))?,
|
||||
));
|
||||
}
|
||||
CliprdrState::Initialization => {
|
||||
// During initialization state, first copy action is synthetic and should be sent along with
|
||||
// capabilities and temporary directory PDUs.
|
||||
pdus.push(ClipboardPdu::Capabilities(self.capabilities.clone()));
|
||||
pdus.push(ClipboardPdu::TemporaryDirectory(
|
||||
ClientTemporaryDirectory::new(self.backend.temporary_directory())
|
||||
.map_err(|e| encode_err!(e))?,
|
||||
));
|
||||
pdus.push(ClipboardPdu::FormatList(
|
||||
self.build_format_list(available_formats).map_err(|e| encode_err!(e))?,
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
error!(?self.state, "Attempted to initiate copy in incorrect state");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -337,9 +335,10 @@ impl<R: Role> SvcProcessor for Cliprdr<R> {
|
|||
self.backend.on_file_contents_response(response);
|
||||
Ok(Vec::new())
|
||||
}
|
||||
_ => self.handle_error_transition(ClipboardError::UnimplementedPdu {
|
||||
pdu: pdu.message_name(),
|
||||
}),
|
||||
ClipboardPdu::TemporaryDirectory(_) => {
|
||||
// do nothing
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<'_> {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
|
||||
## [[0.8.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-connector-v0.7.1...ironrdp-connector-v0.8.0)] - 2025-12-18
|
||||
|
||||
### <!-- 7 -->Build
|
||||
|
||||
- Bump picky and sspi ([#1028](https://github.com/Devolutions/IronRDP/issues/1028)) ([5bd319126d](https://github.com/Devolutions/IronRDP/commit/5bd319126d32fbd8e505508e27ab2b1a18a83d04))
|
||||
|
||||
This fixes build issues with some dependencies.
|
||||
|
||||
## [[0.7.1](https://github.com/Devolutions/IronRDP/compare/ironrdp-connector-v0.7.0...ironrdp-connector-v0.7.1)] - 2025-09-04
|
||||
|
||||
### <!-- 1 -->Features
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "ironrdp-connector"
|
||||
version = "0.7.1"
|
||||
version = "0.8.0"
|
||||
readme = "README.md"
|
||||
description = "State machines to drive an RDP connection sequence"
|
||||
edition.workspace = true
|
||||
|
|
@ -27,13 +27,13 @@ ironrdp-core = { path = "../ironrdp-core", version = "0.1" } # public
|
|||
ironrdp-error = { path = "../ironrdp-error", version = "0.1" } # public
|
||||
ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6", features = ["std"] } # public
|
||||
arbitrary = { version = "1", features = ["derive"], optional = true } # public
|
||||
sspi = "0.16" # public
|
||||
sspi = { version = "0.18", features = ["scard"] }
|
||||
url = "2.5" # public
|
||||
rand = { version = "0.9", features = ["std"] } # TODO: dependency injection?
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
picky-asn1-der = "0.5"
|
||||
picky-asn1-x509 = "0.14"
|
||||
picky = "7.0.0-rc.17"
|
||||
picky-asn1-x509 = "0.15"
|
||||
picky = "=7.0.0-rc.20" # FIXME: We are pinning with = because the candidate version number counts as the minor number by Cargo, and will be automatically bumped in the Cargo.lock.
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use picky_asn1_x509::{oids, Certificate, ExtensionView, GeneralName};
|
|||
use sspi::credssp::{self, ClientState, CredSspClient};
|
||||
use sspi::generator::{Generator, NetworkRequest};
|
||||
use sspi::negotiate::ProtocolConfig;
|
||||
use sspi::Secret;
|
||||
use sspi::Username;
|
||||
use tracing::debug;
|
||||
|
||||
|
|
@ -123,11 +124,13 @@ impl CredsspSequence {
|
|||
certificate: cert,
|
||||
reader_name: config.reader_name.clone(),
|
||||
card_name: None,
|
||||
container_name: config.container_name.clone(),
|
||||
container_name: Some(config.container_name.clone()),
|
||||
csp_name: config.csp_name.clone(),
|
||||
pin: pin.as_bytes().to_vec().into(),
|
||||
private_key_file_index: None,
|
||||
private_key: Some(key.into()),
|
||||
scard_type: sspi::SmartCardType::Emulated {
|
||||
scard_pin: Secret::new(pin.as_bytes().to_vec()),
|
||||
},
|
||||
};
|
||||
sspi::Credentials::SmartCard(Box::new(identity))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,13 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
|
||||
## [[0.6.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-futures-v0.5.0...ironrdp-futures-v0.6.0)] - 2025-12-18
|
||||
|
||||
|
||||
## [[0.1.3](https://github.com/Devolutions/IronRDP/compare/ironrdp-futures-v0.1.2...ironrdp-futures-v0.1.3)] - 2025-03-12
|
||||
|
||||
### <!-- 7 -->Build
|
||||
|
||||
- Update dependencies (#695) ([c21fa44fd6](https://github.com/Devolutions/IronRDP/commit/c21fa44fd6f3c6a6b74788ff68e83133c1314caa))
|
||||
|
||||
|
||||
## [[0.1.2](https://github.com/Devolutions/IronRDP/compare/ironrdp-futures-v0.1.1...ironrdp-futures-v0.1.2)] - 2025-01-28
|
||||
|
||||
### <!-- 6 -->Documentation
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "ironrdp-futures"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
readme = "README.md"
|
||||
description = "`Framed*` traits implementation above futures’s traits"
|
||||
edition.workspace = true
|
||||
|
|
@ -17,7 +17,7 @@ test = false
|
|||
|
||||
[dependencies]
|
||||
futures-util = { version = "0.3", features = ["io"] } # public
|
||||
ironrdp-async = { path = "../ironrdp-async", version = "0.7" } # public
|
||||
ironrdp-async = { path = "../ironrdp-async", version = "0.8" } # public
|
||||
bytes = "1" # public
|
||||
|
||||
[lints]
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
|
||||
## [[0.7.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-graphics-v0.6.0...ironrdp-graphics-v0.7.0)] - 2025-12-18
|
||||
|
||||
### Added
|
||||
|
||||
- [**breaking**] `InvalidIntegralConversion` variant in `RlgrError` and `ZgfxError`
|
||||
|
||||
### <!-- 7 -->Build
|
||||
|
||||
- Bump bytemuck from 1.23.2 to 1.24.0 ([#1008](https://github.com/Devolutions/IronRDP/issues/1008)) ([a24a1fa9e8](https://github.com/Devolutions/IronRDP/commit/a24a1fa9e8f1898b2fcdd41d87660ab9e38f89ed))
|
||||
|
||||
## [[0.6.0](https://github.com/Devolutions/IronRDP/compare/ironrdp-graphics-v0.5.0...ironrdp-graphics-v0.6.0)] - 2025-06-27
|
||||
|
||||
### <!-- 4 -->Bug Fixes
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "ironrdp-graphics"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
readme = "README.md"
|
||||
description = "RDP image processing primitives"
|
||||
edition.workspace = true
|
||||
|
|
@ -22,14 +22,13 @@ bitvec = "1.0"
|
|||
ironrdp-core = { path = "../ironrdp-core", version = "0.1" } # public
|
||||
ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.6", features = ["std"] } # public
|
||||
byteorder = "1.5" # TODO: remove
|
||||
lazy_static.workspace = true # Legacy crate; prefer std::sync::LazyLock or LazyCell
|
||||
num-derive.workspace = true # TODO: remove
|
||||
num-traits.workspace = true # TODO: remove
|
||||
yuv = { version = "0.8", features = ["rdp"] }
|
||||
|
||||
[dev-dependencies]
|
||||
bmp = "0.5"
|
||||
bytemuck = "1.23"
|
||||
bytemuck = "1.24"
|
||||
expect-test.workspace = true
|
||||
|
||||
[lints]
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)] // It’s hard to find better names for co, cg, etc.
|
||||
let sample_shift = params.chroma_subsampling as usize;
|
||||
#![allow(clippy::similar_names, reason = "it’s hard to find better names for co, cg, etc")]
|
||||
|
||||
let sample_shift = usize::from(params.chroma_subsampling);
|
||||
|
||||
let (y_offset, co_offset, cg_offset) = (
|
||||
self.color_plane_offsets[0],
|
||||
|
|
@ -265,12 +266,13 @@ fn ycocg_with_cll_to_rgb(cll: u8, y: u8, co: u8, cg: u8) -> Rgb {
|
|||
// |R| |1 1/2 -1/2| |Y |
|
||||
// |G| = |1 0 1/2| * |Co|
|
||||
// |B| |1 -1/2 -1/2| |Cg|
|
||||
let chroma_shift = (cll - 1) as usize;
|
||||
let chroma_shift = cll - 1;
|
||||
|
||||
let clip_i16 = |v: i16| v.clamp(0, 255) as u8;
|
||||
let clip_i16 =
|
||||
|v: i16| u8::try_from(v.clamp(0, 255)).expect("fits into u8 because the value is clamped to [0..256]");
|
||||
|
||||
let co_signed = (co << chroma_shift) as i8;
|
||||
let cg_signed = (cg << chroma_shift) as i8;
|
||||
let co_signed = (co << chroma_shift).cast_signed();
|
||||
let cg_signed = (cg << chroma_shift).cast_signed();
|
||||
|
||||
let y = i16::from(y);
|
||||
let co = i16::from(co_signed);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ hyper-util = { version = "0.1", features = ["tokio"] }
|
|||
hyper = { version = "1.7", features = ["client", "http1"] }
|
||||
ironrdp-core = { path = "../ironrdp-core", version = "0.1", features = ["std"] }
|
||||
ironrdp-error = { path = "../ironrdp-error", version = "0.1" }
|
||||
ironrdp-tls = { path = "../ironrdp-tls", version = "0.1" }
|
||||
ironrdp-tls = { path = "../ironrdp-tls", version = "0.2" }
|
||||
log = "0.4"
|
||||
tokio-tungstenite = { version = "0.27" }
|
||||
tokio-tungstenite = { version = "0.28" }
|
||||
tokio-util = { version = "0.7" }
|
||||
tokio = { version = "1.43", features = ["macros", "rt"] }
|
||||
uuid = { version = "1.16", features = ["v4"] }
|
||||
uuid = { version = "1.19", features = ["v4"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue