feat(rdpeudp): carry tunnel sub-headers through the UDP transport - #2030
Greg Lamberson (glamberson) wants to merge 4 commits into
Conversation
|
This pull request may overlap with #2009. PR This notice is advisory only. Automated review continues as usual, and how these pull requests relate is for maintainers and authors to decide. Note LLM-assisted content (no human feedback). |
There was a problem hiding this comment.
The PR plumbs RDPEMT Tunnel Data sub-headers through the tokio UDP transport via a new TunnelMessage, additively adding send_message/recv_message and SubHeadersTooLarge while preserving send/recv behavior. I independently verified the synchronous guard matches TunnelData::encode's HeaderLength constraint (4 + sub-headers <= 255, also covering the per-sub-header u8 limit), that ironrdp-client and FramedRead keep their observable behavior, and that no protocol defect is introduced. Published: the new message API still has no in-repo consumer outside tests (open question pending the announced follow-up server PR), the unused Default derive on TunnelMessage, and a now-stale FramedRead comment claiming the tunnel consumes sub-headers. Rejected the TunnelData-deduplication suggestion as a deliberate layering choice.
- [skeptical] New TunnelMessage/send_message/recv_message API has no consumer outside this crate's tests — low 🟡 ❓ — crates/ironrdp-rdpeudp-tokio/src/transport.rs
Verified in the pr-head tree: send_message, recv_message and TunnelMessage are referenced only by this crate's unit tests and the new full-stack test; ironrdp-client/src/rdp.rs still uses transport.send/transport.recv, and FramedRead for UdpTransport is built on recv(), which drops sub-headers. So the capability this PR exists to enable ([MS-RDPBCGR] 1.3.9 auto-detect sub-headers reaching an application) is reachable only from tests until the follow-up server PR announced in the PR body lands, which the provided context cannot confirm. The change is additive and behavior-preserving with the deferred-dispatch intent documented, so this stays a low-severity open question rather than a defect. - [general] FramedRead comment about the tunnel consuming sub-headers is now stale — low 🟡 — crates/ironrdp-rdpeudp-tokio/src/framed.rs
The PR now forwards sub-headers to the application channel inside TunnelMessage, and nothing consumes them before FramedRead's recv(), which drops them. The comment stating 'The tunnel has already taken what it needs from those subheaders by the time we get here' described the old tunnel_data_loop behavior and is no longer true, so it misleads maintainers about where sub-headers go. The skip-empty-payload behavior itself is unchanged and correct; only the justification is stale. The comment lines predate this PR, so no line range is cited.
|
Thanks for the review. The |
There was a problem hiding this comment.
The PR correctly surfaces RDP_TUNNEL_SUBHEADER content in both directions via the new TunnelMessage: the write pump encodes message.sub_headers, the data loop forwards received sub_headers, and send/recv keep their behavior. Verified against the head tree: the send_message precheck (4 + summed wire_size > 255) exactly matches ironrdp-rdpemt's TunnelData encoder invariant, with tests pinning the 255/256 boundary; PayloadTooLarge still correctly covers only higher-layer data; sub-headers stay bundled with their PDU's data in FIFO order; the new error variant is additive on a #[non_exhaustive] enum. No correctness, protocol, or API defect found. The only publishable concern is a low-severity cross-crate drift hazard: the precheck restates the rdpemt encoder's private bound with a hard-coded 4. The From<Vec<u8>> style suggestion was rejected as a defensible ergonomics choice.
- [skeptical + code-compressor] send_message precheck re-implements the rdpemt HeaderLength bound with a hard-coded header size — low 🟡 — crates/ironrdp-rdpeudp-tokio/src/transport.rs
The `4 /* RDP_TUNNEL_HEADER */` literal and the `4 + sub_headers_len > 255` rule duplicate the invariant owned by ironrdp-rdpemt's TunnelData encoder (header_length_untruncated = pub(crate) TunnelHeader::MIN_SIZE + summed wire_size, refused beyond the one-byte HeaderLength field). The two agree today and the 255/256-byte tests pin the boundary, but if rdpemt's header size or bound ever changes, the precheck could accept a message the write pump's encode then rejects — the exact per-call-failure behavior this check exists to guarantee. A follow-up exposing a checked sizing helper from ironrdp-rdpemt would give the invariant one owner; that cross-crate public-API change is out of this PR's scope.
504d3f4 to
78baf60
Compare
|
Thanks for the second review. The send_message precheck now has a comment citing MS-RDPEMT 2.2.1.1 for the one byte HeaderLength that counts the 4 byte fixed part, and the boundary test also runs the largest accepted message (249 bytes of sub-header data) and the smallest refused one (250) through the real TunnelData encoder, so the precheck and the encoder cannot drift apart unnoticed. Changing either side alone now fails the test. I left the sizing helper in ironrdp-rdpemt out, since it would be a public API change in another crate that this test makes unnecessary. |
The RDPEMT codec already handled sub-headers, but UdpTransport exposed only a PDU's data, so the Continuous Auto-Detection messages MS-RDPBCGR 1.3.9 carries there could be neither sent nor received. TunnelMessage carries both; send_message and recv_message use it, and send/recv keep working on data alone.
The sub-header examples carried a whole Bandwidth Measure Start, header bytes included. An auto-detect sub-header is the auto-detect structure itself (MS-RDPEMT 2.2.1.1.1): its SubHeaderLength and SubHeaderType are the structure's headerLength and headerTypeId, so on the wire those examples would repeat the header. The transport treats the data as opaque and the tests passed either way; the examples now carry only what follows the header.
…ment Nothing builds a default TunnelMessage, and a default one would be an empty Tunnel Data PDU. The FramedRead comment said the tunnel consumes the sub-headers before this point; recv drops them, and a caller that needs them uses recv_message.
The send_message precheck restates the HeaderLength bound that ironrdp-rdpemt's TunnelData encoder owns. The boundary test now also runs the largest accepted message and the smallest refused one through the real encoder, so the two cannot drift apart unnoticed, and the comment on the check cites MS-RDPEMT 2.2.1.1.
Head branch was pushed to by a user without write access
13f95f6 to
efe1777
Compare
|
Rebased onto master. #1954 added a cloneable |
Summary
UdpTransportonly ever sent an empty list and dropped the ones it received. MS-RDPBCGR 1.3.9 carries Continuous Auto-Detection for a sideband channel in use in those sub-headers (Bandwidth Measure Start and Stop, Network Characteristics Result, Bandwidth Measure Results), so none of it could be sent or received over UDP.TunnelMessageholds a PDU's data and its sub-headers.send_messageandrecv_messageuse it;sendandrecvkeep their signatures and behavior,recvdropping any sub-headers as before.send_messagechecks the sub-headers against the one-byteHeaderLengthsynchronously, like the existing payload-length check, so an oversized message fails at the call instead of ending the write pump. NewUdpTransportErrorKind::SubHeadersTooLargefor it.UdpTransportErrorKindis#[non_exhaustive].Validation
cargo xtask check fmt/lints/tests/typos/locksall pass. New tests cover the write pump encoding sub-headers, the data loop forwarding them,recvagainstrecv_message, the size check at 255 and 256 bytes, and a full-stack loopback round trip including a message with sub-headers and no data.Notes
The server side uses this in #2031 to measure bandwidth over the tunnel, and the client side in #2009.