rdproxy prototype fork of Devolutions/IronRDP; upstream https://github.com/Devolutions/IronRDP
  • Rust 91%
  • C# 5.2%
  • TypeScript 2.8%
  • PowerShell 0.4%
  • Svelte 0.3%
Find a file
Greg Lamberson 1723385068
feat(rdpeudp)!: add the connection state machine (#1681)
Fourth of six. Filed against master directly; #1626, #1627 and #1679
have
all merged.

Adds the sans-I/O `RdpeudpConnection` and the machinery it drives: send
and
receive windows, loss detection, NewReno congestion control, an RFC 6298
RTT
estimator, the timer table, the reliability controller that matches
retransmissions to the packets they replace, and sequence-number
reconstruction
from 16-bit wire values.

This is the largest PR in the stack. I looked at splitting the state
machine
from the reliability primitives, but `connection.rs` drives all of them
and the
tests span both, so the seam would have been artificial.

## Sans-I/O

No clock, no socket. Time arrives as a `MonotonicInstant` argument and
outgoing
packets leave as `Transmit` values. That keeps it testable without a
network,
and avoids `std::time::Instant`, whose `now` panics on
`wasm32-unknown-unknown`.

**`MonotonicInstant` is defined here, and #1530 adds one to
`ironrdp-connector`
for the same reason.** Two copies of one abstraction is a wart.
`ironrdp-core`
looks like the right home to me, and I raised it on #140; happy to move
it
wherever you prefer, in this PR or a follow-up.

## Behaviour that is required rather than chosen

**Version 3 in the SYN.** That is the version [MS-RDPEUDP] 1.3.2.2 and
the
2.2.2.9 table tie to the MS-RDPEUDP2 data transfer. Version 2 selects
the
MS-RDPEUDP one, which this crate does not implement. Version 3 requires
the
SHA-256 of the `securityCookie` in the client's SYN (2.2.2.9), so
`ConnectionConfig` carries it, `connect` refuses without it, and the
server
performs the check 3.1.5.1.1 asks for.

**The handshake retransmits.** 1.3.1 delivers the SYN, SYN+ACK and ACK
by
persistent retransmits whatever mode the transport runs in; 3.1.5.4.1
gives up
after between three and five unanswered tries. A repeated datagram from
the peer
draws a repeat of ours rather than an error, including a SYN+ACK
arriving after
we have moved on to v2, which is how a client learns its final ACK was
lost.

**The delayed-ACK timer tracks the RTT instead of a fixed duration.**
MS-RDPEUDP2 3.1.5.2 gives half the round trip time as the receiver's
default.
The handshake round trip seeds the existing RFC 6298 estimator (skipped
when
the handshake datagram was retransmitted, per Karn's algorithm), and the
computed timeout is clamped to the 50-200ms band [MS-RDPEUDP] 3.1.6.3
gives a
version-2 connection, since MS-RDPEUDP2 itself states no floor or cap of
its
own.

**`UdpVersion` widens from a closed enum to a newtype carrying the raw
wire
value.** This is a breaking change to the public API #1627 shipped.
MS-RDPEUDP
1.7 and 3.1.5.1.3 require a responder to negotiate down to a version it
supports when the peer advertises one it does not recognize;
hard-failing
decode on an unrecognized value made that MUST clause unsatisfiable.
Every
call site already used the named constants, so this is the only
consequential
part of the change.

**`error.rs` and its `Cargo.toml`/README wiring are back.** #1627
correctly
dropped them at its own narrower, PDU-only scope; this PR's state
machine is
what actually needs them.

**The receive window advances on both events in 3.1.1.2.2**, not only on
AckOfAcks. The first one is what fires on a connection that is losing
nothing;
without it the window fills one window in and stops accepting.

**AckOfAcks carries our own lowest unacknowledged sequence number**
(2.2.1.2.4,
3.1.5.3). It is the only thing that can move a receiver past a packet
the sender
gave up on, since the retransmission carries a fresh `DataSeqNum` and
the
original is never filled.

**Writes are split to the MTU.** MS-RDPEUDP2 does not segment:
3.1.1.2.4.2
forwards each packet's payload straight up and nothing marks a first or
last
fragment. `ChannelSeqNum` looks like it would serve but 3.1.5.5 gives it
a
different job, matching a retransmission to the packet it replaces. So
anything
longer than one packet is split before it becomes packets.

**Dummy packets** are accounted for by the transport and their contents
dropped,
per 3.1.1.1.5.

## Test plan

`cargo xtask check fmt/lints/tests/typos/locks`

179 rdpeudp tests in `ironrdp-testsuite-core` with this PR applied, plus
179
inline unit tests across the crate's components. The connection tests
cover a
clean transfer longer than the window, a loss in the middle of one,
handshake
retransmission to the give-up limit, the version and cookie negotiation
paths,
decoding an unrecognized `uUdpVer`, the ack-delay timeout's RTT-tracking
and
clamping behaviour, and the review round's findings: an out-of-range ACK
no
longer discards outstanding data, the ACK vector respects its 127-entry
wire
limit, `log_window_size` is validated, the ACK builders encode real
timestamps and gaps, the handshake's Karn's-algorithm check is ordered
correctly, the retransmit timer restarts on real progress, `accept`
completes
the negotiate-down behaviour, and `send` enforces a buffer bound.
2026-08-16 21:00:39 -04:00
.agents/skills feat(rdpei): add Input DVC and ActiveX touch (#1647) 2026-08-13 08:04:05 -04:00
.cargo build(deps): bump getrandom to 0.3 2025-07-24 06:34:50 -04:00
.github ci(pr-automation): allow oversized review opt-in (#1680) 2026-08-17 02:04:47 +09:00
benches chore(deps): normalize version requirements to minimal form (#1288) 2026-05-19 14:15:04 +00:00
crates feat(rdpeudp)!: add the connection state machine (#1681) 2026-08-16 21:00:39 -04:00
ffi feat(activex): negotiate monitor topology (#1675) 2026-08-16 13:42:43 -04:00
fuzz feat(rdpdr): add filesystem PDU foundation (#1566) 2026-08-07 13:48:09 -04:00
testing/agentic-rdp feat(agent): add authorized RDPDR harness (#1620) 2026-08-10 16:40:41 -04:00
web-client feat(rdcleanpath): support Hyper-V connection ordering (#1505) 2026-08-11 11:42:17 -04:00
xtask ci: protect Cargo test settings (#1650) 2026-08-13 22:37:25 +09:00
.gitattribute feat(ffi): initial C# bindings (connector only) (#423) 2024-04-05 15:11:32 +00:00
.gitignore chore: remove code coverage pipeline (#1324) 2026-05-27 13:29:13 +00:00
AGENTS.md refactor: move inline tests to file ends (#1527) 2026-08-03 13:52:13 +00:00
ARCHITECTURE.md feat(replay): export replay frame sequences (#1669) 2026-08-15 11:19:55 -04:00
Cargo.lock feat(rdpeudp)!: add the connection state machine (#1681) 2026-08-16 21:00:39 -04:00
Cargo.toml build(deps): bump num-derive from 0.4.2 to 0.5.1 in /fuzz (#1540) 2026-08-05 18:20:39 +09:00
CLAUDE.md chore: add AGENTS.md with repository manual for AI coding agents (#1106) 2026-02-17 15:53:12 +00:00
cliff.toml chore(release): prepare for publishing (#656) 2025-01-31 04:22:55 +00:00
clippy.toml fix(error): box diagnostic metadata to shrink Error<Kind> size (#1269) 2026-05-14 13:57:23 +03:00
LICENSE-APACHE ironrdp: initial commit 2019-07-10 17:31:26 -04:00
LICENSE-MIT ironrdp: initial commit 2019-07-10 17:31:26 -04:00
README.md docs: streamline project README (#1594) 2026-08-09 20:38:36 +09:00
release-plz.toml chore(release): prepare for publishing (#1364) 2026-07-10 15:38:47 +00:00
rust-toolchain.toml chore: update Rust toolchain to 1.94.1 (#1495) 2026-08-01 01:21:46 +09:00
rustfmt.toml style: run cargo fmt 2022-04-15 13:39:34 -04:00
STYLE.md docs(style): define log-level policy for library crates 2026-06-23 22:52:52 +09:00
typos.toml feat(rdpeudp)!: add the connection state machine (#1681) 2026-08-16 21:00:39 -04:00

IronRDP

A Rust implementation of the Microsoft Remote Desktop Protocol, with a focus on security.

crates.io docs.rs CI License: MIT OR Apache-2.0 Matrix

IronRDP is a modular Rust implementation of RDP, the protocol behind Windows Remote Desktop. It is not a monolithic client: its composable crates provide PDU codecs, connection and session state machines, virtual channels, and image codecs for native, WebAssembly, and .NET clients, servers, and proxies. The continuously fuzzed, no_std-compatible core performs no I/O, so applications supply the transport and runtime.

Highlights

  • Sans-I/O core: Drive connection and session state machines with blocking I/O, tokio, futures, or your own event loop.
  • Security first: Core crates are fuzzed, unsafe is heavily linted, and the workspace enforces strict correctness lints.
  • Runs everywhere: The same protocol core powers native binaries, WebAssembly modules, and C#/.NET bindings.
  • Client and server: Connect to Windows hosts or expose your own desktop through the acceptor and server skeleton.
  • Modular: Enable only the crates and features you need without pulling in unrelated subsystems.

Features

Protocol and security

  • RDP connection sequence: X.224 negotiation, MCS, capability exchange, licensing, reactivation
  • Enhanced RDP Security with TLS (1.2 and 1.3)
  • Network Level Authentication (NLA) via CredSSP, with NTLM and Kerberos
  • KDC proxy and RDCleanPath support for gateway-mediated, just-in-time connections
  • Terminal Services Gateway (MS-TSGU) transport
  • .rdp file parsing and writing, plus a typed configuration property store

Graphics

  • Client-side decoding: uncompressed raw bitmaps, Interleaved RLE, RDP 6.0 bitmap compression, and RemoteFX (RFX)
  • Server-side encoding: RDP 6.0 bitmap compression, RemoteFX, optional NSCodec, and optional QOI / QOI+zstd
  • Additional codec primitives available as libraries: ClearCodec, RemoteFX Progressive, ZGFX, and the graphics pipeline (EGFX) PDUs
  • Bulk compression: MPPC, NCRUSH, and XCRUSH

Virtual channels

  • Static (SVC) and dynamic (DVC / DRDYNVC) channel infrastructure
  • Clipboard redirection (CLIPRDR), audio output (RDPSND), device and smart card redirection (RDPDR)
  • Display control for dynamic resizing, echo (RTT probes), alternative input, and USB redirection
  • Windows DVC COM plugin loader and a DVC named-pipe proxy for bridging external processes

Targets and bindings

  • Native clients on Windows, macOS, and Linux
  • WebAssembly bindings plus a protocol-agnostic web component (@devolutions/iron-remote-desktop)
  • C#/.NET bindings generated with Diplomat (Devolutions.IronRdp)

Getting started

Prebuilt binaries

Checksummed .tar.gz archives are attached to each GitHub release, one per supported platform:

  • ironrdp-viewer - a windowed RDP client (tags ironrdp-viewer-v*)
  • ironrdp-agent - a daemon-backed CLI for automation (tags ironrdp-agent-v*)

Each release includes download, checksum, and extraction instructions.

Install with Cargo

cargo install ironrdp-viewer
cargo install ironrdp-agent

Both binaries link native audio, so Linux builds need the ALSA development headers (libasound2-dev on Debian/Ubuntu) and Windows builds need NASM.

ironrdp-viewer

ironrdp-viewer is a portable, windowed RDP client with asynchronous I/O and software rendering.

ironrdp-viewer <HOSTNAME> --username <USERNAME> --password <PASSWORD>

Omitted credentials are prompted for interactively. You can also load a .rdp file:

ironrdp-viewer --rdp-file ./my-server.rdp

Set IRONRDP_LOG to adjust logging, for example IRONRDP_LOG="info,ironrdp_connector=trace". See the viewer README for supported .rdp properties, TLS key logging, and the full option list.

ironrdp-agent

ironrdp-agent combines a long-lived RDP daemon with short-lived CLI invocations for scripts and LLM-driven automation:

ironrdp-agent daemon-start --overlay ./credentials.rdp        # in one terminal
ironrdp-agent connect --server <HOSTNAME> --username <USER>   # in another
ironrdp-agent screenshot ./desktop.png

connect fails with missing required fields unless credentials are available. Preload credentials with daemon-start --overlay <FILE> to keep secrets away from the IPC caller, or pass --password to connect. Run ironrdp-agent --help-agent for a machine-readable description of every operation. See the agent README for the IPC format, secret handling, and remote execution support.

Using IronRDP as a library

Add the meta crate and enable only the pieces you need:

[dependencies]
ironrdp = { version = "0.17", features = ["connector", "session", "graphics"] }

Each feature maps to a standalone crate, so you can depend on ironrdp-pdu, ironrdp-connector, ironrdp-session, and related crates directly. API documentation is available on docs.rs.

Two runnable examples ship with the meta crate:

# Connect, decode the desktop, and write a PNG. Blocking, synchronous I/O.
cargo run --example=screenshot -- --host <HOSTNAME> -u <USERNAME> -p <PASSWORD> -o out.png

# A minimal RDP server built on ironrdp-server.
cargo run --example=server -- --bind-addr 127.0.0.1:3389

Tips

Enabling RemoteFX on a Windows server

Run the following PowerShell commands, then reboot:

Set-ItemProperty -Path 'HKLM:\Software\Policies\Microsoft\Windows NT\Terminal Services' -Name 'ColorDepth' -Type DWORD -Value 5
Set-ItemProperty -Path 'HKLM:\Software\Policies\Microsoft\Windows NT\Terminal Services' -Name 'fEnableVirtualizedGraphics' -Type DWORD -Value 1

Alternatively, enable these group policies with gpedit.msc and reboot. They are under Computer Configuration → Administrative Templates → Windows Components → Remote Desktop Services → Remote Desktop Session Host → Remote Session Environment:

  1. RemoteFX for Windows Server 2008 R2 → Configure RemoteFX
  2. Enable RemoteFX encoding for RemoteFX clients designed for Windows Server 2008 R2 SP1
  3. Limit maximum color depth

Who uses IronRDP

  • Devolutions Gateway for browser-based and native RDP client access
  • Cloudflare Access for browser-based RDP
  • Teleport for remote desktop web access
  • Lamco RDP Server, a Wayland-native RDP server for Linux desktop sharing
  • MacRDP, a native RDP server for macOS
  • qemu-rdp, an RDP server for QEMU displays
  • A growing set of community projects building RDP servers and clients on the crate suite

Rust version (MSRV)

IronRDP's MSRV is the oldest of three versions: the latest stable Rust release at least six months old, the version packaged by Fedora stable, and the version available in Debian stable-backports. rust-toolchain.toml pins both the project toolchain and the MSRV validated by CI. See the architecture policy for details.

Contributing

Contributions are welcome; start with ARCHITECTURE and STYLE, and keep changes scoped. Project automation uses xtask following the cargo xtask convention. Run cargo xtask --help for the command list and cargo xtask bootstrap to install development requirements. Run cargo xtask ci before opening a pull request; it covers everything CI runs except FFI and .NET checks, which have separate cargo xtask ffi commands.

Workspace builds use the native prerequisites listed under Install with Cargo. The web client also needs Node.js >= 24 LTS, and the FFI bindings need the .NET SDK.

AI-assisted development

AI-assisted development is welcome, but contributors remain responsible for understanding, reviewing, and validating every change. For RDP protocol work, install the Windows Protocols skill from awakecoding/openspecs so agents can navigate the Microsoft Open Specifications corpus.

Getting help

License

Licensed under either MIT or Apache-2.0 at your option.