feat(client): expose generic session configuration and lifecycle APIs - #1522
Merged
Marc-André Moreau (mamoreau-devolutions) merged 8 commits intoAug 2, 2026
Merged
Conversation
Marc-André Moreau (mamoreau-devolutions)
force-pushed
the
copilot/client-lifecycle-apis
branch
from
August 2, 2026 21:43
d04467e to
c70ba4e
Compare
Copilot started reviewing on behalf of
Marc-André Moreau (mamoreau-devolutions)
August 2, 2026 22:15
View session
Marc-André Moreau (mamoreau-devolutions)
force-pushed
the
copilot/client-lifecycle-apis
branch
from
August 2, 2026 22:15
0ab8cf0 to
14edb22
Compare
Marc-André Moreau (mamoreau-devolutions)
force-pushed
the
copilot/client-lifecycle-apis
branch
from
August 2, 2026 22:16
14edb22 to
8606b16
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Exposes generic RDP client configuration, bounded input delivery, lifecycle events, clipboard integration, and Display Control resize fallback behavior across consumers.
Changes:
- Adds generic configuration and runtime channel APIs.
- Adds cancellable bounded input, lifecycle notifications, and resize fallback handling.
- Includes TLS validation changes despite the stated stack boundary.
Reviewed changes
Copilot reviewed 22 out of 23 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
xtask/src/check.rs |
Adds native-TLS test execution. |
testing/agentic-rdp/Connect-AgentSession.ps1 |
Configures test certificate policy and readiness checks. |
ffi/src/session/mod.rs |
Exposes session-info output. |
ffi/src/connector/config.rs |
Supplies the connection profile. |
crates/ironrdp/examples/screenshot.rs |
Updates example configuration. |
crates/ironrdp-web/src/session.rs |
Handles lifecycle output and connection profile. |
crates/ironrdp-viewer/src/clipboard.rs |
Uses bounded clipboard input. |
crates/ironrdp-viewer/src/app.rs |
Adopts bounded input and lifecycle events. |
crates/ironrdp-tls/tests/native_tls.rs |
Tests native-TLS validation policies. |
crates/ironrdp-tls/tests/certs/server-key.pem |
Adds the TLS test key. |
crates/ironrdp-tls/tests/certs/server-cert.pem |
Adds the TLS test certificate. |
crates/ironrdp-tls/src/stub.rs |
Adds validation-policy stubs. |
crates/ironrdp-tls/src/rustls.rs |
Implements Rustls validation policies. |
crates/ironrdp-tls/src/native_tls.rs |
Implements native-TLS validation policies. |
crates/ironrdp-tls/src/lib.rs |
Exposes certificate-validation APIs. |
crates/ironrdp-tls/README.md |
Documents validation behavior. |
crates/ironrdp-tls/Cargo.toml |
Adds roots and test dependencies. |
crates/ironrdp-testsuite-extra/tests/e2e.rs |
Adds TLS and configuration coverage. |
crates/ironrdp-testsuite-extra/tests/client_config.rs |
Tests generic builder options. |
crates/ironrdp-testsuite-core/tests/session/connection_activation.rs |
Updates test configuration. |
crates/ironrdp-testsuite-core/tests/connector/autodetect.rs |
Updates test configuration. |
crates/ironrdp-session/src/x224/mod.rs |
Surfaces session-info notifications. |
crates/ironrdp-session/src/active_stage.rs |
Adds display readiness and lifecycle output. |
crates/ironrdp-dvc/src/client.rs |
Adds DVC registration inspection. |
crates/ironrdp-connector/src/lib.rs |
Adds configurable connection type. |
crates/ironrdp-connector/src/connection.rs |
Advertises the selected connection type. |
crates/ironrdp-client/src/rdp.rs |
Implements bounded input, lifecycle, and resize handling. |
crates/ironrdp-client/src/config.rs |
Expands generic client configuration. |
crates/ironrdp-client/src/clipboard.rs |
Adopts bounded clipboard delivery. |
crates/ironrdp-client/README.md |
Documents TLS configuration. |
crates/ironrdp-client/Cargo.toml |
Marks TLS as a public dependency. |
crates/ironrdp-agent/src/daemon.rs |
Adopts generic configuration and lifecycle APIs. |
crates/ironrdp-agent/src/cli.rs |
Documents certificate policy configuration. |
crates/ironrdp-agent/Cargo.toml |
Adds the TLS dependency. |
Cargo.lock |
Updates dependency metadata. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Marc-André Moreau (mamoreau-devolutions)
force-pushed
the
copilot/client-lifecycle-apis
branch
from
August 2, 2026 23:26
f64a244 to
2ae15fe
Compare
Marc-André Moreau (mamoreau-devolutions)
merged commit Aug 2, 2026
57b1366
into
master
27 checks passed
Marc-André Moreau (mamoreau-devolutions)
deleted the
copilot/client-lifecycle-apis
branch
August 2, 2026 23:36
Marc-André Moreau (mamoreau-devolutions)
pushed a commit
that referenced
this pull request
Aug 3, 2026
…uto-reconnect cookie (#1501) > **Rebased onto post-#1522 master.** #1509 landed the server half of #1508 while this was open, including the `ClientAutoReconnect` structure. This PR no longer declares it; it extends it, and picks up the parts #1509 did not build. ## What The client half of automatic reconnection. The session layer surfaces the Server Auto-Reconnect Cookie, `ironrdp-pdu` derives and verifies the client's response to it, and the connector sends that response when resuming a session. ## Why A client whose connection drops ungracefully can reattach to its session instead of making the user log on again, provided it returns the cookie the server issued during logon ([MS-RDPBCGR] 1.3.1.5). #1509 built the server side of that: it validates a returning `ARC_CS_PRIVATE_PACKET` and rotates the random. Nothing answers it. `ironrdp-session` decodes the cookie and drops it, `ironrdp-connector` has no way to send one back, and `TODO(#271)` still sits in `ironrdp-client`. So `ironrdp-client` cannot resume a session against `ironrdp-server`, and the validation #1509 added has no in-tree counterpart to exercise it. The wire encoding was already there. `ExtendedClientOptionalInfo` carries, encodes and decodes a 28-byte `autoReconnectCookie` and its builder already had a `reconnect_cookie` step; `ServerAutoReconnect` already decoded; #1509 added `ClientAutoReconnect` and its decode. Nothing connected them. ## The three parts **Receive.** `SaveSessionInfo` now also surfaces the cookie, as `ProcessorOutput::AutoReconnectCookie` and `ActiveStageOutput::AutoReconnectCookie`. #1522 added a `SaveSessionInfo { logon_complete }` output on that same handler; the two coexist rather than compete, since both are read off one PDU and neither supersedes the other. The handler emits the logon notification unconditionally and appends the cookie when one is present, and a test pins that surfacing the cookie does not suppress the notification. #1509's server replaces the cookie whenever a client connects and again hourly ([MS-RDPBCGR] 3.3.6.2), so this can arrive more than once in a session and the consumer keeps the most recent. **Derive.** `ClientAutoReconnect::from_server_cookie` implements [MS-RDPBCGR] 5.5: > The auto-reconnect random is used to key the HMAC function ([RFC2104]), which uses MD5 as the iterative hash function. The security verifier is derived by applying the HMAC to the client random received in Step 3. > > `SecurityVerifier = HMAC(AutoReconnectRandom, ClientRandom)` > > When Enhanced RDP Security is in effect the client random value is not generated (section 5.3.2). In this case, for the purpose of generating the security verifier, the client random is assumed to be an array of 32 zero bytes. IronRDP implements no Standard RDP Security path (there is no Security Exchange PDU), so the zero-client-random case is the only one that arises. As 5.5 notes, that makes the verifier constant for a given cookie, so it proves possession of the cookie and nothing more; session security comes from the outer TLS/CredSSP handshake. @clintcan independently confirmed this construction against real **mstsc** while validating #1509 ([comment](#1509 (comment))): a Windows client's `ARC_CS_PRIVATE_PACKET` verifies against `HMAC-MD5(random_bits, [0u8; 32])`. That is the same derivation implemented here, so the two halves interoperate with Microsoft's client and not only with each other. **Send.** `ClientConnector::with_auto_reconnect_cookie` takes the cookie last received and makes the connector put the derived Client Auto-Reconnect Packet ([MS-RDPBCGR] 2.2.4.3) in the Client Info PDU. Absent, that PDU is byte-for-byte what it was. Unlike the server packet, this structure has no enclosing logon-info field header, so it encodes to exactly the 28 bytes the cookie field expects. `to_bytes` writes that layout directly rather than going through `Encode`, so filling a fixed-size field has no error path a caller must handle; a test pins the two to agree. ## One derivation, not two Putting `from_server_cookie` in `ironrdp-pdu` would leave the workspace with two implementations of 5.5, since #1509 added a private HMAC to `ironrdp-server`. So `ClientAutoReconnect` also gains `verify`, and the server routes through it. `verify` keeps the constant-time comparison the server had. The verifier is the whole credential, so a comparison returning early on the first differing byte would let a peer recover it a byte at a time from the timing; the session identifier is not secret and is compared normally. `ironrdp-server` keeps the policy around the check, which cookies are live and whether the security protocol permits auto-reconnect, and drops its `hmac` and `md-5` dependencies. `hmac` moves to `ironrdp-pdu` as `default-features = false`; the crate's full feature powerset still checks clean, including `--no-default-features`. I would rather not have reached into `ironrdp-server` in a `pdu,session,connector` change, but the alternative was shipping the duplicate and filing a follow-up to remove it, which is a worse trade for reviewer time. ## Tests that were not running That move also rehomes the known-answer tests @clintcan contributed on #1509. They went in as an inline `#[cfg(test)]` module in `crates/ironrdp-server/src/server.rs`, and that crate sets `[lib] test = false`, so they have never executed in CI. They now live in `ironrdp-testsuite-core` against the public API, where CI runs them: his HMAC-MD5 reference vector is kept as a second vector alongside a differently-keyed one, plus the cases for a tampered verifier and a mismatched logon ID. Worth flagging separately: `ironrdp-server` is not alone. `ironrdp-agent`, `ironrdp-session` and `ironrdp-web` also set `[lib] test = false` and between them carry 16 files of inline `#[cfg(test)]` modules that CI never runs. That is out of scope here, but I am happy to open an issue if it would be useful. ## Breaking changes `ActiveStageOutput` and `x224::ProcessorOutput` gain a variant, and `ClientConnector` gains a public field, so exhaustive matches and struct literals need updating. Confirmed with `cargo-semver-checks` against the merge-base: those three are the only findings this branch introduces. The others it reports on `master` today (`ShareDataPdu::Compressed` and the `ShareDataCtx` fields from #1518, `ProcessorBuilder.bulk_decompressor` from #1518, `ServerEvent::SetAutoReconnectCookie` from #1509) are present on `master` unchanged. The `ironrdp-pdu` additions are additive. ## Scope This is the library half. `ironrdp-client`, `ironrdp-web` and the FFI bindings gain an arm for the new output but none of them reconnect automatically yet; that is the remaining part of #271, and the existing `TODO(#271)` in `ironrdp-client` marks where it goes. I kept receive, derive and send together deliberately. Split up, none of them is usable on its own: without the receive half there is no way to obtain a cookie, and without the send half there is nothing to do with one. ## Tests Thirteen, all in `ironrdp-testsuite-core`. On the packet and the derivation: the `SecurityVerifier` matches two independently computed HMAC-MD5 vectors of 32 zero bytes under different keys, so the tests pin the derivation rather than restating the code; the logon ID carries over from the server cookie; the encoding matches the 2.2.4.3 field layout byte for byte with `cbLen` fixed at `0x1C`; `to_bytes` agrees with `Encode`; it round-trips; and it rejects both a wrong packet length and an unknown version. On verification: a derived answer is accepted, a single flipped byte in the verifier is rejected, a correct verifier under a different logon ID is rejected, and an answer derived from a different random is rejected. On the surfacing path: a Save Session Info PDU framed the way a server sends it, through the real x224 processor, yields an `AutoReconnectCookie` carrying the right logon ID and random bits, alongside #1522's logon notification rather than in place of it; and one without a cookie surfaces no cookie. ## Verification `cargo xtask check fmt/lints/tests/typos/locks` all pass on 1.94.1, including a `fuzz/` build before the lock check. ## Note #1496 also touches the `ClientAutoReconnect` declaration. Whichever of the two lands second needs a one-line rebase on the derive attribute; happy to take that in either order. [MS-RDPBCGR]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/e729948a-3f4e-4568-9aef-d355e30b5389 [RFC2104]: https://www.rfc-editor.org/rfc/rfc2104
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stack dependencies
This PR is stacked on
copilot/tls-validation-policy(b2bbcece), which already includes the merged runtime static-channel support frommaster. It intentionally contains no TLS implementation/policy, ActiveX/COM, SVC implementation, decompression, or bitmap-recovery changes.Validation
cargo fmt --check --allcargo xtask check tests --no-run -vcargo xtask check lints -vcargo test -p ironrdp-client --lib --features rustlscargo check -p ironrdp-agent -p ironrdp-viewer -p ironrdp-web -p ffi