Skip to content

fix(connector): stay in CapabilitiesExchange when activation handles DeactivateAll#1371

Open
Rocco De Angelis (rdeangel) wants to merge 1 commit into
Devolutions:masterfrom
rdeangel:fix/connector-capabilities-exchange-same-state
Open

fix(connector): stay in CapabilitiesExchange when activation handles DeactivateAll#1371
Rocco De Angelis (rdeangel) wants to merge 1 commit into
Devolutions:masterfrom
rdeangel:fix/connector-capabilities-exchange-same-state

Conversation

@rdeangel

Copy link
Copy Markdown

Fixes #1362

Problem

#1254 taught the inner ConnectionActivationSequence to skip a Server Deactivate All PDU received before Server Demand Active (sent by e.g. Windows Server and gnome-remote-desktop as part of a Deactivation-Reactivation Sequence, MS-RDPBCGR 1.3.1.3): it returns Written::Nothing and remains in CapabilitiesExchange.

However, the outer ClientConnector::step() only accepts ConnectionFinalization as the resulting inner state after calling connection_activation.step(), so the same scenario still fails with invalid state (this is a bug) when it happens during the initial connection sequence — exactly the symptom reported in #1362.

Changes

  • Add the missing match arm in ClientConnector::step(): when the inner sequence stays in CapabilitiesExchange, the outer connector stays in CapabilitiesExchange too and waits for the next input.
  • Add a regression test mirroring the existing fix(connector): handle ServerDeactivateAll during CapabilitiesExchange #1254 tests, driving the outer ClientConnector instead of the inner sequence (fails with invalid state before the fix).

🤖 Generated with Claude Code

…DeactivateAll

PR Devolutions#1254 taught the inner ConnectionActivationSequence to skip a
Server Deactivate All PDU received before Server Demand Active (sent
by e.g. Windows Server and gnome-remote-desktop during a
Deactivation-Reactivation Sequence, MS-RDPBCGR 1.3.1.3): it returns
Written::Nothing and stays in CapabilitiesExchange. However, the outer
ClientConnector::step() only accepted ConnectionFinalization as the
resulting inner state, so the same scenario still failed with
"invalid state (this is a bug)" during the initial connection
sequence. Mirror the inner behavior in the outer state machine and
keep waiting for the Demand Active PDU.

Fixes Devolutions#1362

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mahcsig

Copy link
Copy Markdown

Rocco De Angelis (@rdeangel) thank you for looking into this!

I've tried compiling again from your branch and it does get past the ServerDectivateAll, but it now errors out saying [decode_send_data_indication @ /code/src/github.com/rdeangel/IronRDP/crates/ironrdp-connector/src/lib.rs:409] reason: received disconnect provider ultimatum: UserRequested

@rdeangel

Copy link
Copy Markdown
Author

Thanks for testing again, good to see it's getting past ServerDeactivateAll now.

From the error location, the connection is getting quite far: it's past NLA and licensing, into capabilities exchange. It handles the ServerDeactivateAll and waits for a ServerDemandActive, but the server sends a disconnectProviderUltimatum and drops instead. So I don't think this PR is causing the disconnect, it just lets the connection get far enough to reach it, where before it stopped at the "invalid state" error, but I'd rather not guess at why the server is dropping without more to go on.

A couple of things that would really help narrow it down:

  • What server is this (Windows version / GNOME RD / xrdp / other)?
  • Does the same server connect OK with another client like FreeRDP or mstsc? That'd tell us whether it's something specific to IronRDP or a server-side decision.
  • If you can grab a trace log (or Wireshark capture) of the PDUs right after the ServerDeactivateAll, that'd show whether the server sends anything before the disconnect.

One small note: the UserRequested name is a little misleading, it's just the generic MCS code for a deliberate close, not necessarily anything a user did.

Separately, this did make me wonder if it'd be worth surfacing DisconnectProviderUltimatum as a typed error instead of a string, so it's easier to handle distinctly but that's independent of this PR.

@mahcsig

Copy link
Copy Markdown

That makes sense, to help narrow it down:

  • the server is Ubuntu 24.0.4 LTS, using the gnome-remote-desktop service
    • win11 connects properly, so it may be something specific gnome is doing?
  • mstsc is able to connect to the same server
  • I'm not sure how to start the capture after the ServerDeactivateAll, but here is the updated pcap of the connection attempt after the fix: rdp-ironrdp.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Invalid state error using gnome-remote-desktop

2 participants