Add Rust RDP decoder library (#61858)

This library makes the IronRDP fast path decoder available to Go code
via CGo. There are three build options:

1. A standalone library (for use by tsh), enabled with the rust_rdp_decoder
   build tag.
2. All functionality included in the existing RDP client library,
   enabled with the desktop_access_rdp build tag.
3. A no-op implementation which avoids any Rust dependencies.

The decoder also respects the RDPCLIENT_SKIP_BUILD=1 flag from the makefile,
under the assumption that if you don't have Rust tooling and don't want
to build the RDP client that you also don't want the decoder.
This commit is contained in:
Zac Bergquist
2026-01-18 18:40:04 +00:00
committed by GitHub
parent 843ed647e6
commit 07aa756126
16 changed files with 514 additions and 23 deletions
+12 -15
View File
@@ -3,16 +3,18 @@ run-name: Build on Windows
on:
merge_group:
# We only build tsh and tctl on Windows so only consider Go code
# (tsh and tctl don't run any Rust)
paths:
- '.github/workflows/build-windows.yaml'
- '**.go'
- 'go.mod'
- 'go.sum'
- 'build.assets/Makefile'
- 'build.assets/Dockerfile*'
- 'Makefile'
- ".github/workflows/build-windows.yaml"
- "**.go"
- "go.mod"
- "go.sum"
- "build.assets/Makefile"
- "build.assets/Dockerfile*"
- "Makefile"
- "Cargo.toml"
- "Cargo.lock"
- "lib/srv/desktop/rdp/decoder/Cargo.toml"
- "lib/srv/desktop/rdp/decoder/src/**.rs"
jobs:
build:
@@ -27,16 +29,11 @@ jobs:
- name: Checkout Teleport
uses: actions/checkout@v6
- name: Get Go version
id: go-version
shell: bash
run: echo "go-version=$(make -s print-go-version | tr -d '\n')" >> $GITHUB_OUTPUT
- name: Setup Go
uses: actions/setup-go@v6
with:
cache: false
go-version: ${{ steps.go-version.outputs.go-version }}
go-version-file: go.mod
- name: Build
shell: bash
Generated
+10
View File
@@ -2304,6 +2304,7 @@ dependencies = [
"picky-asn1-der",
"picky-asn1-x509",
"rand 0.9.2",
"rdp-decoder",
"reqwest",
"rsa",
"rustls",
@@ -2316,6 +2317,15 @@ dependencies = [
"uuid",
]
[[package]]
name = "rdp-decoder"
version = "0.1.0"
dependencies = [
"ironrdp-core",
"ironrdp-graphics",
"ironrdp-session",
]
[[package]]
name = "redox_syscall"
version = "0.5.2"
+1 -2
View File
@@ -1,6 +1,6 @@
[workspace]
resolver = "2"
members = ["lib/srv/desktop/rdp/rdpclient", "web/packages/shared/libs/ironrdp"]
members = ["lib/srv/desktop/rdp/rdpclient", "lib/srv/desktop/rdp/decoder", "web/packages/shared/libs/ironrdp"]
[workspace.package]
edition = "2021"
@@ -36,4 +36,3 @@ ironrdp-tls = { git = "https://github.com/Devolutions/IronRDP", rev = "a0a3e750c
"rustls",
] }
ironrdp-tokio = { git = "https://github.com/Devolutions/IronRDP", rev = "a0a3e750c9e4ee9c73b957fbcb26dbc59e57d07d" }
+15 -4
View File
@@ -131,14 +131,16 @@ CARGO_TARGET_linux_arm := arm-unknown-linux-gnueabihf
CARGO_TARGET_linux_arm64 := aarch64-unknown-linux-gnu
CARGO_TARGET_linux_386 := i686-unknown-linux-gnu
CARGO_TARGET_linux_amd64 := x86_64-unknown-linux-gnu
CARGO_TARGET_windows_amd64 := x86_64-pc-windows-gnu
CARGO_TARGET := --target=$(RUST_TARGET_ARCH)
CARGO_WASM_TARGET := wasm32-unknown-unknown
# If set to 1, Windows RDP client is not built.
# If set to 1, then we don't build the desktop access RDP client
# or the RDP decoder for tsh.
RDPCLIENT_SKIP_BUILD ?= 0
# Enable Windows RDP client build?
# Enable Rust RDP support?
with_rdpclient := no
RDPCLIENT_MESSAGE := without-Windows-RDP-client
@@ -160,6 +162,7 @@ ifneq ("$(is_fips_on_arm64)","yes")
with_rdpclient := yes
RDPCLIENT_MESSAGE := with-Windows-RDP-client
RDPCLIENT_TAG := desktop_access_rdp
TSH_RDP_DECODER_TAG := rust_rdp_decoder
endif
endif
endif
@@ -392,11 +395,11 @@ $(BUILDDIR)/teleport: ensure-webassets rdpclient
# NOTE: Any changes to the `tsh` build here must be copied to `build.assets/windows/build.ps1`
# until we can use this Makefile for native Windows builds.
.PHONY: $(BUILDDIR)/tsh
$(BUILDDIR)/tsh:
$(BUILDDIR)/tsh: rdpdecoder
@if [[ "$(OS)" != "windows" && -z "$(LIBFIDO2_BUILD_TAG)" ]]; then \
echo 'Warning: Building tsh without libfido2. Install libfido2 to have access to MFA.' >&2; \
fi
GOOS=$(OS) GOARCH=$(ARCH) $(CGOFLAG_TSH) go build -tags "$(FIPS_TAG) $(LIBFIDO2_BUILD_TAG) $(TOUCHID_TAG) $(PIV_BUILD_TAG) $(VNETDAEMON_TAG) $(KUSTOMIZE_NO_DYNAMIC_PLUGIN)" -o $(BUILDDIR)/tsh $(BUILDFLAGS) $(TOOLS_LDFLAGS) ./tool/tsh
GOOS=$(OS) GOARCH=$(ARCH) $(CGOFLAG_TSH) go build -tags "$(FIPS_TAG) $(LIBFIDO2_BUILD_TAG) $(TOUCHID_TAG) $(PIV_BUILD_TAG) $(VNETDAEMON_TAG) $(TSH_RDP_DECODER_TAG) $(KUSTOMIZE_NO_DYNAMIC_PLUGIN)" -o $(BUILDDIR)/tsh $(BUILDFLAGS) $(TOOLS_LDFLAGS) ./tool/tsh
.PHONY: $(BUILDDIR)/tbot
# tbot is CGO-less by default except on Windows because lib/client/terminal/ wants CGO on this OS
@@ -499,6 +502,13 @@ ifeq ("$(with_rdpclient)", "yes")
cargo build -p rdp-client $(if $(FIPS),--features=fips) --release --locked $(CARGO_TARGET)
endif
.PHONY: rdpdecoder
rdpdecoder: rustup-toolchain-warning
ifeq ("$(with_rdpclient)", "yes")
$(RDPCLIENT_ENV) \
cargo build -p rdp-decoder --release --locked $(CARGO_TARGET)
endif
define ironrdp_package_json
{
"name": "ironrdp",
@@ -1363,6 +1373,7 @@ ADDLICENSE_COMMON_ARGS := -c 'Gravitational, Inc.' \
-ignore 'gen/**' \
-ignore 'gitref.go' \
-ignore 'lib/srv/desktop/rdp/rdpclient/target/**' \
-ignore 'lib/srv/desktop/rdp/decoder/target/**' \
-ignore 'lib/web/build/**' \
-ignore 'target/**' \
-ignore 'web/packages/design/src/assets/icomoon/style.css' \
+2 -1
View File
@@ -425,8 +425,9 @@ function Build-Tsh {
$CommandDuration = Measure-Block {
Write-Host "::group::Building tsh..."
cargo build -p rdp-decoder --release --locked
$UnsignedBinaryPath = "$BuildDirectory\unsigned-$BinaryName"
go build -tags piv -trimpath -ldflags "-s -w $BuildTypeLDFlags" -o "$UnsignedBinaryPath" "$TeleportSourceDirectory\tool\tsh"
go build -tags "piv rust_rdp_decoder" -trimpath -ldflags "-s -w $BuildTypeLDFlags" -o "$UnsignedBinaryPath" "$TeleportSourceDirectory\tool\tsh"
if ($LastExitCode -ne 0) {
exit $LastExitCode
}
+1
View File
@@ -248,6 +248,7 @@ require (
go.opentelemetry.io/proto/otlp v1.9.0
golang.org/x/crypto v0.46.0
golang.org/x/exp v0.0.0-20250813145105-42675adae3e6
golang.org/x/image v0.33.0
golang.org/x/mod v0.31.0
golang.org/x/net v0.48.0
golang.org/x/oauth2 v0.34.0
+2
View File
@@ -2429,6 +2429,8 @@ golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeap
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
golang.org/x/image v0.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ=
golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "rdp-decoder"
version = "0.1.0"
edition.workspace = true
license.workspace = true
publish.workspace = true
[lib]
crate-type = ["staticlib", "lib"]
[dependencies]
ironrdp-core.workspace = true
ironrdp-graphics.workspace = true
ironrdp-session.workspace = true
+165
View File
@@ -0,0 +1,165 @@
//go:build desktop_access_rdp || rust_rdp_decoder
/*
* Teleport
* Copyright (C) 2025 Gravitational, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Package decoder implements a RDP fast path decoder by calling into IronRDP via CGo.
//
// When used by Teleport (ie with the desktop_access_rdp build tag), this package links
// its symbols from librdp (the Rust library that Teleport already links).
//
// When used by other client tools (tsh), we link to a separate librdp_decoder library
// (using the rust_rdp_decoder build tag) to avoid linking in extra RDP code that we don't need.
//
// If neither build tag is specified, then a no-op implementation is used.
package decoder
/*
#cgo nocallback rdp_decoder_new
#cgo noescape rdp_decoder_new
#cgo nocallback rdp_decoder_free
#cgo noescape rdp_decoder_free
#cgo nocallback rdp_decoder_resize
#cgo noescape rdp_decoder_resize
#cgo nocallback rdp_decoder_process
#cgo noescape rdp_decoder_process
#cgo nocallback rdp_decoder_image_data
#cgo noescape rdp_decoder_image_data
#include <stdint.h>
typedef struct RdpDecoder RdpDecoder;
RdpDecoder* rdp_decoder_new(uint16_t width, uint16_t height);
void rdp_decoder_free(RdpDecoder* ptr);
void rdp_decoder_resize(RdpDecoder* ptr, uint16_t width, uint16_t height);
void rdp_decoder_process(RdpDecoder* ptr, const uint8_t* data, size_t len);
const uint8_t* rdp_decoder_image_data(RdpDecoder* ptr, uint16_t* width, uint16_t* height);
*/
import "C"
import (
"errors"
"image"
"math"
"unsafe"
"golang.org/x/image/draw"
)
type Decoder struct {
ptr *C.RdpDecoder
}
func New(width, height uint16) (*Decoder, error) {
ptr := C.rdp_decoder_new(C.uint16_t(width), C.uint16_t(height))
if ptr == nil {
return nil, errors.New("failed to create decoder")
}
return &Decoder{ptr: ptr}, nil
}
func (d *Decoder) Release() {
if d.ptr == nil {
return
}
C.rdp_decoder_free(d.ptr)
d.ptr = nil
}
func (d *Decoder) Resize(width, height uint16) {
if d.ptr == nil {
return
}
C.rdp_decoder_resize(d.ptr, C.uint16_t(width), C.uint16_t(height))
}
// Process processes an RDP fast path frame, updating the state of
// the decoder and its internal frame buffer.
func (d *Decoder) Process(frame []byte) {
if d.ptr == nil {
return
}
data := unsafe.SliceData(frame)
C.rdp_decoder_process(d.ptr, (*C.uint8_t)(data), C.size_t(len(frame)))
}
// Image produces an RGBA image for the current state of the screen.
// Callers should check that the resulting image is not nil before
// attempting to operate on it.
func (d *Decoder) Image() *image.RGBA {
if d == nil || d.ptr == nil {
return nil
}
var outWidth, outHeight C.uint16_t
data := C.rdp_decoder_image_data(d.ptr, &outWidth, &outHeight)
if data == nil || outWidth == 0 || outHeight == 0 {
return nil
}
rgba := image.NewRGBA(image.Rect(0, 0, int(outWidth), int(outHeight)))
// Copy from the Rust-owned memory into Go memory.
copy(rgba.Pix, unsafe.Slice((*uint8)(data), int(outWidth)*int(outHeight)*4))
return rgba
}
// Thumbnail produces a scaled image of the current state of the screen.
// It uses a low-quality interpolator so it shouldn't be used for large
// size images.
func (d *Decoder) Thumbnail(width, height int) *image.RGBA {
fullSize := d.Image()
if fullSize == nil || width <= 0 || height <= 0 {
return nil
}
srcBounds := fullSize.Bounds()
srcW := srcBounds.Dx()
srcH := srcBounds.Dy()
if srcW == 0 || srcH == 0 {
return nil
}
// Compute scale to fit the source inside the requested thumbnail
// while preserving aspect ratio.
scaleW := float64(width) / float64(srcW)
scaleH := float64(height) / float64(srcH)
scale := math.Min(scaleW, scaleH)
// Calculate destination size after scaling.
dstW := int(math.Max(1, math.Floor(float64(srcW)*scale+0.5)))
dstH := int(math.Max(1, math.Floor(float64(srcH)*scale+0.5)))
thumbnail := image.NewRGBA(image.Rect(0, 0, width, height))
// Center the scaled image within the thumbnail.
offsetX := (width - dstW) / 2
offsetY := (height - dstH) / 2
dstRect := image.Rect(offsetX, offsetY, offsetX+dstW, offsetY+dstH)
// Note: the nearest neighbor interpolator is fast, but produces the lowest quality
// results. We're okay with this for thumbnails.
draw.NearestNeighbor.Scale(thumbnail, dstRect, fullSize, srcBounds, draw.Over, nil)
return thumbnail
}
@@ -0,0 +1,37 @@
//go:build !desktop_access_rdp && !rust_rdp_decoder
// Teleport
// Copyright (C) 2025 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package decoder
import (
"image"
"github.com/gravitational/trace"
)
type Decoder struct{}
func New(width, height uint16) (*Decoder, error) {
return nil, trace.NotImplemented("the RDP decoder is not included in this build")
}
func (d *Decoder) Release() {}
func (d *Decoder) Resize(w, h uint16) {}
func (d *Decoder) Process([]byte) {}
func (d *Decoder) Image() *image.RGBA { return nil }
func (d *Decoder) Thumbnail(w, h int) *image.RGBA { return nil }
@@ -0,0 +1,21 @@
//go:build desktop_access_rdp
// Teleport
// Copyright (C) 2025 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package decoder
import _ "github.com/gravitational/teleport/lib/srv/desktop/rdp/rdpclient"
@@ -0,0 +1,35 @@
//go:build !desktop_access_rdp && rust_rdp_decoder
// Teleport
// Copyright (C) 2025 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package decoder
/*
#cgo linux,386 LDFLAGS: -L${SRCDIR}/../../../../../target/i686-unknown-linux-gnu/release
#cgo linux,amd64 LDFLAGS: -L${SRCDIR}/../../../../../target/x86_64-unknown-linux-gnu/release
#cgo linux,arm LDFLAGS: -L${SRCDIR}/../../../../../target/arm-unknown-linux-gnueabihf/release
#cgo linux,arm64 LDFLAGS: -L${SRCDIR}/../../../../../target/aarch64-unknown-linux-gnu/release
#cgo linux LDFLAGS: -lrdp_decoder
#cgo darwin,amd64 LDFLAGS: -L${SRCDIR}/../../../../../target/x86_64-apple-darwin/release
#cgo darwin,arm64 LDFLAGS: -L${SRCDIR}/../../../../../target/aarch64-apple-darwin/release
#cgo darwin LDFLAGS: -lrdp_decoder
#cgo windows,amd64 LDFLAGS: -L${SRCDIR}/../../../../../target/x86_64-pc-windows-gnu/release
#cgo windows LDFLAGS: -lrdp_decoder
*/
import "C"
+194
View File
@@ -0,0 +1,194 @@
// Teleport
// Copyright (C) 2025 Gravitational, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! This crate contains an RDP decoder which can produce images given
//! a series of RDP fast path PDUs. It exposes a small C API so that
//! Go (via cgo) can create a decoder and call its methods.
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::ptr;
use ironrdp_core::WriteBuf;
use ironrdp_graphics::image_processing::PixelFormat;
use ironrdp_session::{
fast_path::{Processor, ProcessorBuilder},
image::DecodedImage,
};
pub struct RdpDecoder {
image: DecodedImage,
fast_path_processor: Processor,
}
impl RdpDecoder {
const PIXEL_FORMAT: PixelFormat = PixelFormat::RgbA32;
pub fn new(width: u16, height: u16) -> Self {
Self {
image: DecodedImage::new(RdpDecoder::PIXEL_FORMAT, width, height),
fast_path_processor: ProcessorBuilder {
// These options only matter in a real RDP session when we have
// to send responses back to the server. We can safely leave them
// at defaults when decoding session recordings.
io_channel_id: 0,
user_channel_id: 0,
enable_server_pointer: false,
pointer_software_rendering: false,
}
.build(),
}
}
pub fn resize(&mut self, width: u16, height: u16) {
self.image = DecodedImage::new(RdpDecoder::PIXEL_FORMAT, width, height);
}
pub fn image_data(&self) -> &[u8] {
self.image.data()
}
pub fn width(&self) -> u16 {
self.image.width()
}
pub fn height(&self) -> u16 {
self.image.height()
}
pub fn process(&mut self, tdp_fast_path_frame: &[u8]) {
let mut output = WriteBuf::new();
// In a live RDP connection, this would return data that we need
// to use to create reponses to send to the server.
// We're only interested in updating the internal frame buffer,
// so we can ignore the result.
let _ = self
.fast_path_processor
.process(&mut self.image, tdp_fast_path_frame, &mut output);
}
}
/// Create a new decoder and return an owned pointer to it.
/// The caller is responsible for calling rdp_decoder_free
/// when the decoder is no longer needed.
#[no_mangle]
pub extern "C" fn rdp_decoder_new(width: u16, height: u16) -> *mut RdpDecoder {
match catch_unwind_and_drop_panic_payload(AssertUnwindSafe(move || {
Box::into_raw(Box::new(RdpDecoder::new(width, height)))
})) {
Ok(ptr) => ptr,
Err(_) => ptr::null_mut(),
}
}
/// Frees the memory associated with a decoder.
///
/// # Safety
///
/// `ptr` must be a pointer allocated by `rdp_decoder_new`.
#[no_mangle]
pub unsafe extern "C" fn rdp_decoder_free(ptr: *mut RdpDecoder) {
if ptr.is_null() {
return;
}
let _ = catch_unwind_and_drop_panic_payload(AssertUnwindSafe(move || unsafe {
let _ = Box::from_raw(ptr);
}));
}
/// Resizes the decoder's internal image buffer.
///
/// # Safety
///
/// - `ptr` must be a valid, non-null pointer previously returned by `rdp_decoder_new`
///
/// Note: Resizing replaces the decoder's internal image buffer; any previously obtained
/// pointers into the internal buffer (for example via `rdp_decoder_image_data`) may become
/// invalid after this call.
#[no_mangle]
pub unsafe extern "C" fn rdp_decoder_resize(ptr: *mut RdpDecoder, width: u16, height: u16) {
if ptr.is_null() {
return;
}
let _ = catch_unwind_and_drop_panic_payload(AssertUnwindSafe(move || unsafe {
let decoder = &mut *ptr;
decoder.resize(width, height);
}));
}
/// Processes an RDP fast path frame and updates the internal state of the frame buffer.
///
/// # Safety
///
/// - `ptr` must be a valid pointer previously returned by `rdp_decoder_new` and
/// - `data` must point to `len` contiguous bytes that are readable by this function.
/// Passing a null `data` pointer with `len > 0` is invalid. If `len == 0` the function
/// returns immediately and no bytes are read.
#[no_mangle]
pub unsafe extern "C" fn rdp_decoder_process(ptr: *mut RdpDecoder, data: *const u8, len: usize) {
if ptr.is_null() || data.is_null() || len == 0 {
return;
}
let _ = catch_unwind_and_drop_panic_payload(AssertUnwindSafe(move || unsafe {
let decoder = &mut *ptr;
let slice = std::slice::from_raw_parts(data, len);
decoder.process(slice);
}));
}
/// Returns a pointer to the decoder's internal image buffer. The `out_width` and `out_height`
/// outparams receive the size of the image in pixels.
///
///
/// # Safety
///
/// The returned pointer is valid as long as the decoder is alive and is not mutated by other calls
/// (e.g. `process`, `resize`). Caller should copy the data if it needs to keep it.
///
/// The returned pointer references out_width * out_height * 4 bytes.
#[no_mangle]
pub unsafe extern "C" fn rdp_decoder_image_data(
ptr: *mut RdpDecoder,
out_width: *mut u16,
out_height: *mut u16,
) -> *const u8 {
const { assert!(matches!(RdpDecoder::PIXEL_FORMAT, PixelFormat::RgbA32)) }
if ptr.is_null() || out_width.is_null() || out_height.is_null() {
return ptr::null();
}
match catch_unwind_and_drop_panic_payload(AssertUnwindSafe(move || unsafe {
let decoder = &*ptr;
let data = decoder.image_data();
*out_width = decoder.image.width();
*out_height = decoder.image.height();
data.as_ptr()
})) {
Ok(p) => p,
Err(_) => ptr::null(),
}
}
fn catch_unwind_and_drop_panic_payload<F: FnOnce() -> R, R>(f: F) -> Result<R, ()> {
catch_unwind(AssertUnwindSafe(f)).map_err(|e| {
// If dropping the original panic payload causes another panic,
// abort the process.
catch_unwind(AssertUnwindSafe(move || std::mem::drop(e)))
.unwrap_or_else(|_e| std::process::abort())
})
}
+1
View File
@@ -30,6 +30,7 @@ iso7816 = "0.1.4"
iso7816-tlv = "0.4.4"
log = "0.4.29"
rand = { version = "0.9.2", features = ["os_rng"] }
rdp-decoder = { path = "../decoder" }
rsa = "0.9.10"
sspi = { version = "0.16.1", features = ["network_client"] }
tokio = { version = "1.48", features = ["full"] }
+3
View File
@@ -24,6 +24,9 @@
//! - Structs for passing between the two (those prefixed with the `#[repr(C)]` macro
//! and whose name begins with `CGO`)
// bring in rdp-decoder to export its unmangled symbols in the staticlib
extern crate rdp_decoder as _;
use crate::client::global::get_client_handle;
use crate::client::Client;
use crate::rdpdr::tdp::SharedDirectoryAnnounce;
+1 -1
View File
@@ -1,3 +1,3 @@
[toolchain]
channel = "1.86.0"
targets = [ "wasm32-unknown-unknown" ]
targets = [ "wasm32-unknown-unknown", "x86_64-pc-windows-gnu" ]